Check checksum of downloaded file if checksum is available
[platform/upstream/curl.git] / src / tool_metalink.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 #include "tool_setup.h"
23
24 #ifdef HAVE_UNISTD_H
25 #  include <unistd.h>
26 #endif
27
28 #include <sys/stat.h>
29
30 #ifdef HAVE_FCNTL_H
31 #  include <fcntl.h>
32 #endif
33
34 #include "rawstr.h"
35
36 #include "tool_metalink.h"
37 #include "tool_getparam.h"
38 #include "tool_paramhlp.h"
39
40 #include "memdebug.h" /* keep this as LAST include */
41
42 /* Copied from tool_getparam.c */
43 #define GetStr(str,val) do { \
44   if(*(str)) { \
45     free(*(str)); \
46     *(str) = NULL; \
47   } \
48   if((val)) \
49     *(str) = strdup((val)); \
50   if(!(val)) \
51     return PARAM_NO_MEM; \
52 } WHILE_FALSE
53
54 #ifdef USE_GNUTLS_NETTLE
55
56 #include <nettle/md5.h>
57 #include <nettle/sha.h>
58
59 typedef struct md5_ctx MD5_CTX;
60
61 static void MD5_Init(MD5_CTX * ctx)
62 {
63   md5_init(ctx);
64 }
65
66 static void MD5_Update(MD5_CTX * ctx,
67                        const unsigned char * input,
68                        unsigned int inputLen)
69 {
70   md5_update(ctx, inputLen, input);
71 }
72
73 static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
74 {
75   md5_digest(ctx, 16, digest);
76 }
77
78 typedef struct sha1_ctx SHA_CTX;
79
80 static void SHA1_Init(SHA_CTX *ctx)
81 {
82   sha1_init(ctx);
83 }
84
85 static void SHA1_Update(SHA_CTX *ctx,
86                         const unsigned char * input,
87                         unsigned int inputLen)
88 {
89   sha1_update(ctx, inputLen, input);
90 }
91
92 static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx)
93 {
94   sha1_digest(ctx, 20, digest);
95 }
96
97 typedef struct sha256_ctx SHA256_CTX;
98
99 static void SHA256_Init(SHA256_CTX *ctx)
100 {
101   sha256_init(ctx);
102 }
103
104 static void SHA256_Update(SHA256_CTX *ctx,
105                           const unsigned char * input,
106                           unsigned int inputLen)
107 {
108   sha256_update(ctx, inputLen, input);
109 }
110
111 static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
112 {
113   sha256_digest(ctx, 32, digest);
114 }
115
116 #else
117
118 #ifdef USE_GNUTLS
119
120 #include <gcrypt.h>
121
122 typedef gcry_md_hd_t MD5_CTX;
123
124 static void MD5_Init(MD5_CTX * ctx)
125 {
126   gcry_md_open(ctx, GCRY_MD_MD5, 0);
127 }
128
129 static void MD5_Update(MD5_CTX * ctx,
130                        const unsigned char * input,
131                        unsigned int inputLen)
132 {
133   gcry_md_write(*ctx, input, inputLen);
134 }
135
136 static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
137 {
138   memcpy(digest, gcry_md_read(*ctx, 0), 16);
139   gcry_md_close(*ctx);
140 }
141
142 typedef gcry_md_hd_t SHA_CTX;
143
144 static void SHA1_Init(SHA_CTX * ctx)
145 {
146   gcry_md_open(ctx, GCRY_MD_SHA1, 0);
147 }
148
149 static void SHA1_Update(SHA_CTX * ctx,
150                        const unsigned char * input,
151                        unsigned int inputLen)
152 {
153   gcry_md_write(*ctx, input, inputLen);
154 }
155
156 static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx)
157 {
158   memcpy(digest, gcry_md_read(*ctx, 0), 20);
159   gcry_md_close(*ctx);
160 }
161
162 typedef gcry_md_hd_t SHA256_CTX;
163
164 static void SHA256_Init(SHA256_CTX * ctx)
165 {
166   gcry_md_open(ctx, GCRY_MD_SHA256, 0);
167 }
168
169 static void SHA256_Update(SHA256_CTX * ctx,
170                        const unsigned char * input,
171                        unsigned int inputLen)
172 {
173   gcry_md_write(*ctx, input, inputLen);
174 }
175
176 static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
177 {
178   memcpy(digest, gcry_md_read(*ctx, 0), 32);
179   gcry_md_close(*ctx);
180 }
181
182 #else
183
184 #ifdef USE_SSLEAY
185
186 #  ifdef USE_OPENSSL
187 #    include <openssl/md5.h>
188 #    include <openssl/sha.h>
189 #  else
190 /* TODO What to do if USE_OPENSSL is undefined? */
191 #  endif
192
193 #else /* USE_SSLEAY */
194
195 /* TODO hash functions for other libraries here */
196
197 #endif /* USE_SSLEAY */
198
199 #endif /* USE_GNUTLS */
200
201 #endif /* USE_GNUTLS_NETTLE */
202
203 const digest_params MD5_DIGEST_PARAMS[] = {
204   {
205     (Curl_digest_init_func) MD5_Init,
206     (Curl_digest_update_func) MD5_Update,
207     (Curl_digest_final_func) MD5_Final,
208     sizeof(MD5_CTX),
209     16
210   }
211 };
212
213 const digest_params SHA1_DIGEST_PARAMS[] = {
214   {
215     (Curl_digest_init_func) SHA1_Init,
216     (Curl_digest_update_func) SHA1_Update,
217     (Curl_digest_final_func) SHA1_Final,
218     sizeof(SHA_CTX),
219     20
220   }
221 };
222
223 const digest_params SHA256_DIGEST_PARAMS[] = {
224   {
225     (Curl_digest_init_func) SHA256_Init,
226     (Curl_digest_update_func) SHA256_Update,
227     (Curl_digest_final_func) SHA256_Final,
228     sizeof(SHA256_CTX),
229     32
230   }
231 };
232
233 digest_context *Curl_digest_init(const digest_params *dparams)
234 {
235   digest_context *ctxt;
236
237   /* Create digest context */
238   ctxt = malloc(sizeof *ctxt);
239
240   if(!ctxt)
241     return ctxt;
242
243   ctxt->digest_hashctx = malloc(dparams->digest_ctxtsize);
244
245   if(!ctxt->digest_hashctx) {
246     free(ctxt);
247     return NULL;
248   }
249
250   ctxt->digest_hash = dparams;
251
252   dparams->digest_init(ctxt->digest_hashctx);
253
254   return ctxt;
255 }
256
257 int Curl_digest_update(digest_context *context,
258                        const unsigned char *data,
259                        unsigned int len)
260 {
261   (*context->digest_hash->digest_update)(context->digest_hashctx, data, len);
262
263   return 0;
264 }
265
266 int Curl_digest_final(digest_context *context, unsigned char *result)
267 {
268   (*context->digest_hash->digest_final)(result, context->digest_hashctx);
269
270   free(context->digest_hashctx);
271   free(context);
272
273   return 0;
274 }
275
276 struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) {
277   struct metalinkfile *f;
278   f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile));
279   f->file = metalinkfile;
280   f->next = NULL;
281   return f;
282 }
283
284 struct metalink *new_metalink(metalink_t *metalink) {
285   struct metalink *ml;
286   ml = (struct metalink*)malloc(sizeof(struct metalink));
287   ml->metalink = metalink;
288   ml->next = NULL;
289   return ml;
290 }
291
292 int count_next_metalink_resource(struct metalinkfile *mlfile)
293 {
294   int count = 0;
295   metalink_resource_t **mlres;
296   for(mlres = mlfile->file->resources; *mlres; ++mlres, ++count);
297   return count;
298 }
299
300 void clean_metalink(struct Configurable *config)
301 {
302   while(config->metalinkfile_list) {
303     struct metalinkfile *mlfile = config->metalinkfile_list;
304     config->metalinkfile_list = config->metalinkfile_list->next;
305     Curl_safefree(mlfile);
306   }
307   config->metalinkfile_last = 0;
308   while(config->metalink_list) {
309     struct metalink *ml = config->metalink_list;
310     config->metalink_list = config->metalink_list->next;
311     metalink_delete(ml->metalink);
312     Curl_safefree(ml);
313   }
314   config->metalink_last = 0;
315 }
316
317 int parse_metalink(struct Configurable *config, const char *infile)
318 {
319   metalink_error_t r;
320   metalink_t* metalink;
321   metalink_file_t **files;
322   struct metalink *ml;
323
324   r = metalink_parse_file(infile, &metalink);
325
326   if(r != 0) {
327     return -1;
328   }
329   if(metalink->files == NULL) {
330     fprintf(config->errors, "\nMetalink does not contain any file.\n");
331     return 0;
332   }
333   ml = new_metalink(metalink);
334
335   if(config->metalink_list) {
336     config->metalink_last->next = ml;
337     config->metalink_last = ml;
338   }
339   else {
340     config->metalink_list = config->metalink_last = ml;
341   }
342
343   for(files = metalink->files; *files; ++files) {
344     struct getout *url;
345     /* Skip an entry which has no resource. */
346     if(!(*files)->resources) {
347       fprintf(config->errors, "\nFile %s does not have any resource.\n",
348               (*files)->name);
349       continue;
350     }
351     if(config->url_get ||
352        ((config->url_get = config->url_list) != NULL)) {
353       /* there's a node here, if it already is filled-in continue to
354          find an "empty" node */
355       while(config->url_get && (config->url_get->flags & GETOUT_URL))
356         config->url_get = config->url_get->next;
357     }
358
359     /* now there might or might not be an available node to fill in! */
360
361     if(config->url_get)
362       /* existing node */
363       url = config->url_get;
364     else
365       /* there was no free node, create one! */
366       url=new_getout(config);
367
368     if(url) {
369       struct metalinkfile *mlfile;
370       /* Set name as url */
371       GetStr(&url->url, (*files)->name);
372
373       /* set flag metalink here */
374       url->flags |= GETOUT_URL | GETOUT_METALINK;
375       mlfile = new_metalinkfile(*files);
376
377       if(config->metalinkfile_list) {
378         config->metalinkfile_last->next = mlfile;
379         config->metalinkfile_last = mlfile;
380       }
381       else {
382         config->metalinkfile_list = config->metalinkfile_last = mlfile;
383       }
384     }
385   }
386   return 0;
387 }
388
389 /*
390  * Returns nonzero if content_type includes mediatype.
391  */
392 static int check_content_type(const char *content_type, const char *media_type)
393 {
394   const char *ptr = content_type;
395   size_t media_type_len = strlen(media_type);
396   for(; *ptr && (*ptr == ' ' || *ptr == '\t'); ++ptr);
397   if(!*ptr) {
398     return 0;
399   }
400   return Curl_raw_nequal(ptr, media_type, media_type_len) &&
401     (*(ptr+media_type_len) == '\0' || *(ptr+media_type_len) == ' ' ||
402      *(ptr+media_type_len) == '\t' || *(ptr+media_type_len) == ';');
403 }
404
405 int check_metalink_content_type(const char *content_type)
406 {
407   return check_content_type(content_type, "application/metalink+xml");
408 }
409
410 static const metalink_digest_def SHA256_DIGEST_DEF[] = {
411   {"sha-256", SHA256_DIGEST_PARAMS}
412 };
413
414 static const metalink_digest_def SHA1_DIGEST_DEF[] = {
415   {"sha-1", SHA1_DIGEST_PARAMS}
416 };
417
418 static const metalink_digest_def MD5_DIGEST_DEF[] = {
419   {"md5", MD5_DIGEST_PARAMS}
420 };
421
422 /*
423  * The alias of supported hash functions in the order by preference
424  * (basically stronger hash comes first). We included "sha-256" and
425  * "sha256". The former is the name defined in the IANA registry named
426  * "Hash Function Textual Names". The latter is widely (and
427  * historically) used in Metalink version 3.
428  */
429 static const metalink_digest_alias digest_aliases[] = {
430   {"sha-256", SHA256_DIGEST_DEF},
431   {"sha256", SHA256_DIGEST_DEF},
432   {"sha-1", SHA1_DIGEST_DEF},
433   {"sha1", SHA1_DIGEST_DEF},
434   {"md5", MD5_DIGEST_DEF},
435   {NULL, NULL}
436 };
437
438 static unsigned char hex_to_uint(const char *s)
439 {
440   int v[2];
441   int i;
442   for(i = 0; i < 2; ++i) {
443     v[i] = Curl_raw_toupper(s[i]);
444     if('0' <= v[i] && v[i] <= '9') {
445       v[i] -= '0';
446     }
447     else if('A' <= v[i] && v[i] <= 'Z') {
448       v[i] -= 'A'-10;
449     }
450   }
451   return (unsigned char)((v[0] << 4) | v[1]);
452 }
453
454 /*
455  * Check checksum of file denoted by filename. The expected hash value
456  * is given in hex_hash which is hex-encoded string.
457  *
458  * This function returns 1 if it succeeds or one of the following
459  * integers:
460  *
461  * 0:
462  *   Checksum didn't match.
463  * -1:
464  *   Could not open file; or could not read data from file.
465  */
466 static int check_hash(const char *filename,
467                       const metalink_digest_def *digest_def,
468                       const char *hex_hash, FILE *error)
469 {
470   unsigned char *result;
471   digest_context *dctx;
472   int check_ok;
473   int fd;
474   size_t i;
475   fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name,
476           filename);
477   fd = open(filename, O_RDONLY);
478   if(fd == -1) {
479     fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno));
480     return -1;
481   }
482   dctx = Curl_digest_init(digest_def->dparams);
483   result = malloc(digest_def->dparams->digest_resultlen);
484   while(1) {
485     unsigned char buf[4096];
486     ssize_t len = read(fd, buf, sizeof(buf));
487     if(len == 0) {
488       break;
489     }
490     else if(len == -1) {
491       fprintf(error, "Could not read file %s: %s\n", filename,
492               strerror(errno));
493       Curl_digest_final(dctx, result);
494       close(fd);
495       return -1;
496     }
497     Curl_digest_update(dctx, buf, (unsigned int)len);
498   }
499   Curl_digest_final(dctx, result);
500   check_ok = 1;
501   for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) {
502     if(hex_to_uint(&hex_hash[i*2]) != result[i]) {
503       check_ok = 0;
504       break;
505     }
506   }
507   free(result);
508   close(fd);
509   return check_ok;
510 }
511
512 int metalink_check_hash(struct Configurable *config,
513                         struct metalinkfile *mlfile,
514                         const char *filename)
515 {
516   metalink_checksum_t **checksum;
517   const metalink_digest_alias *digest_alias;
518   int rv;
519   if(!mlfile->file->checksums) {
520     return -2;
521   }
522   for(digest_alias = digest_aliases; digest_alias->alias_name;
523       ++digest_alias) {
524     for(checksum = mlfile->file->checksums; *checksum; ++checksum) {
525       if(Curl_raw_equal(digest_alias->alias_name, (*checksum)->type) &&
526          strlen((*checksum)->hash) ==
527          digest_alias->digest_def->dparams->digest_resultlen*2) {
528         break;
529       }
530     }
531     if(*checksum) {
532       break;
533     }
534   }
535   if(!digest_alias->alias_name) {
536     fprintf(config->errors, "No supported checksum in Metalink file\n");
537     return -2;
538   }
539   rv = check_hash(filename, digest_alias->digest_def,
540                   (*checksum)->hash, config->errors);
541   if(rv == 1) {
542     fprintf(config->errors, "Checksum matched\n");
543   }
544   else if(rv == 0) {
545     fprintf(config->errors, "Checksum didn't match\n");
546   }
547   return rv;
548 }