a92a2ad11d0987300a12c95566b57c30440bd46f
[oweals/opkg-lede.git] / libopkg / opkg_download.c
1 /* vi: set noexpandtab sw=4 sts=4: */
2 /* opkg_download.c - the opkg package management system
3
4    Carl D. Worth
5
6    Copyright (C) 2001 University of Southern California
7    Copyright (C) 2008 OpenMoko Inc
8
9    This program is free software; you can redistribute it and/or
10    modify it under the terms of the GNU General Public License as
11    published by the Free Software Foundation; either version 2, or (at
12    your option) any later version.
13
14    This program is distributed in the hope that it will be useful, but
15    WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    General Public License for more details.
18 */
19 #include "config.h"
20
21 #ifdef HAVE_CURL
22 #include <curl/curl.h>
23 #endif
24
25 #if defined(HAVE_SSLCURL) || defined(HAVE_OPENSSL)
26 #include <openssl/conf.h>
27 #include <openssl/evp.h>
28 #include <openssl/err.h>
29 #include <openssl/ssl.h>
30 #endif
31
32 #if defined(HAVE_GPGME)
33 #include <gpgme.h>
34 #elif defined(HAVE_OPENSSL)
35 #include <openssl/bio.h>
36 #include <openssl/objects.h>
37 #include <openssl/x509.h>
38 #include <openssl/pem.h>
39 #include <openssl/hmac.h>
40 #endif
41
42 #include "includes.h"
43 #include "opkg_download.h"
44 #include "opkg_message.h"
45
46 #include "sprintf_alloc.h"
47 #include "xsystem.h"
48 #include "file_util.h"
49 #include "opkg_defines.h"
50 #include "libbb/libbb.h"
51
52 #ifdef HAVE_PATHFINDER
53 #include "opkg_pathfinder.h"
54 #endif
55
56 #if defined(HAVE_OPENSSL) || defined(HAVE_SSLCURL)
57 static void openssl_init(void);
58 #endif
59
60 #ifdef HAVE_OPENSSL
61 static X509_STORE *setup_verify(opkg_conf_t *conf, char *CAfile, char *CApath);
62 #endif
63
64 #ifdef HAVE_CURL
65 /*
66  * Make curl an instance variable so we don't have to instanciate it
67  * each time
68  */
69 static CURL *curl = NULL;
70 static CURL *opkg_curl_init(opkg_conf_t *conf, curl_progress_func cb, void *data);
71 #endif
72
73 static int
74 str_starts_with(const char *str, const char *prefix)
75 {
76     return (strncmp(str, prefix, strlen(prefix)) == 0);
77 }
78
79 int opkg_download(opkg_conf_t *conf, const char *src,
80   const char *dest_file_name, curl_progress_func cb, void *data)
81 {
82     int err = 0;
83
84     char *src_basec = xstrdup(src);
85     char *src_base = basename(src_basec);
86     char *tmp_file_location;
87
88     opkg_message(conf,OPKG_NOTICE,"Downloading %s\n", src);
89         
90     if (str_starts_with(src, "file:")) {
91         int ret;
92         const char *file_src = src + 5;
93         opkg_message(conf,OPKG_INFO,"Copying %s to %s...", file_src, dest_file_name);
94         ret = file_copy(src + 5, dest_file_name);
95         opkg_message(conf,OPKG_INFO,"Done\n");
96         free(src_basec);
97         return ret;
98     }
99
100     sprintf_alloc(&tmp_file_location, "%s/%s", conf->tmp_dir, src_base);
101     err = unlink(tmp_file_location);
102     if (err && errno != ENOENT) {
103         opkg_message(conf,OPKG_ERROR, "%s: ERROR: failed to unlink %s: %s\n",
104                 __FUNCTION__, tmp_file_location, strerror(errno));
105         free(tmp_file_location);
106         free(src_basec);
107         return errno;
108     }
109
110     if (conf->http_proxy) {
111         opkg_message(conf,OPKG_DEBUG,"Setting environment variable: http_proxy = %s\n", conf->http_proxy);
112         setenv("http_proxy", conf->http_proxy, 1);
113     }
114     if (conf->ftp_proxy) {
115         opkg_message(conf,OPKG_DEBUG,"Setting environment variable: ftp_proxy = %s\n", conf->ftp_proxy);
116         setenv("ftp_proxy", conf->ftp_proxy, 1);
117     }
118     if (conf->no_proxy) {
119         opkg_message(conf,OPKG_DEBUG,"Setting environment variable: no_proxy = %s\n", conf->no_proxy);
120         setenv("no_proxy", conf->no_proxy, 1);
121     }
122
123 #ifdef HAVE_CURL
124     CURLcode res;
125     FILE * file = fopen (tmp_file_location, "w");
126
127     curl = opkg_curl_init (conf, cb, data);
128     if (curl)
129     {
130         curl_easy_setopt (curl, CURLOPT_URL, src);
131         curl_easy_setopt (curl, CURLOPT_WRITEDATA, file);
132
133         res = curl_easy_perform (curl);
134         fclose (file);
135         if (res)
136         {
137             long error_code;
138             curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &error_code);
139             opkg_message(conf, OPKG_ERROR, "Failed to download %s. \nerror detail: %s\n", src, curl_easy_strerror(res));
140             free(tmp_file_location);
141             free(src_basec);
142             return res;
143         }
144
145     }
146     else
147     {
148         free(tmp_file_location);
149         free(src_basec);
150         return -1;
151     }
152 #else
153     {
154       int res;
155       const char *argv[8];
156       int i = 0;
157
158       argv[i++] = "wget";
159       argv[i++] = "-q";
160       if (conf->http_proxy || conf->ftp_proxy) {
161         argv[i++] = "-Y";
162         argv[i++] = "on";
163       }
164       argv[i++] = "-O";
165       argv[i++] = tmp_file_location;
166       argv[i++] = src;
167       argv[i++] = NULL;
168       res = xsystem(argv);
169
170       if (res) {
171         opkg_message(conf, OPKG_ERROR, "Failed to download %s, error %d\n", src, res);
172         free(tmp_file_location);
173         free(src_basec);
174         return res;
175       }
176     }
177 #endif
178
179     err = file_move(tmp_file_location, dest_file_name);
180
181     free(tmp_file_location);
182     free(src_basec);
183
184     if (err) {
185         return err;
186     }
187
188     return 0;
189 }
190
191 static int opkg_download_cache(opkg_conf_t *conf, const char *src,
192   const char *dest_file_name, curl_progress_func cb, void *data)
193 {
194     char *cache_name = xstrdup(src);
195     char *cache_location, *p;
196     int err = 0;
197
198     if (!conf->cache || str_starts_with(src, "file:")) {
199         err = opkg_download(conf, src, dest_file_name, cb, data);
200         goto out1;
201     }
202
203     for (p = cache_name; *p; p++)
204         if (*p == '/')
205             *p = ',';   /* looks nicer than | or # */
206
207     sprintf_alloc(&cache_location, "%s/%s", conf->cache, cache_name);
208     if (file_exists(cache_location))
209         opkg_message(conf, OPKG_NOTICE, "Copying %s\n", cache_location);
210     else {
211         err = opkg_download(conf, src, cache_location, cb, data);
212         if (err) {
213             (void) unlink(cache_location);
214             goto out2;
215         }
216     }
217
218     err = file_copy(cache_location, dest_file_name);
219
220
221 out2:
222     free(cache_location);
223 out1:
224     free(cache_name);
225     return err;
226 }
227
228 int opkg_download_pkg(opkg_conf_t *conf, pkg_t *pkg, const char *dir)
229 {
230     int err;
231     char *url;
232     char *stripped_filename;
233
234     if (pkg->src == NULL) {
235         opkg_message(conf,OPKG_ERROR, "ERROR: Package %s (parent %s) is not available from any configured src.\n",
236                 pkg->name, pkg->parent->name);
237         return -1;
238     }
239     if (pkg->filename == NULL) {
240         opkg_message(conf,OPKG_ERROR, "ERROR: Package %s (parent %s) does not have a valid filename field.\n",pkg->name, pkg->parent->name);
241         return -1;
242     }
243
244     sprintf_alloc(&url, "%s/%s", pkg->src->value, pkg->filename);
245
246     /* XXX: BUG: The pkg->filename might be something like
247        "../../foo.opk". While this is correct, and exactly what we
248        want to use to construct url above, here we actually need to
249        use just the filename part, without any directory. */
250
251     stripped_filename = strrchr(pkg->filename, '/');
252     if ( ! stripped_filename )
253         stripped_filename = pkg->filename;
254
255     sprintf_alloc(&pkg->local_filename, "%s/%s", dir, stripped_filename);
256
257     err = opkg_download_cache(conf, url, pkg->local_filename, NULL, NULL);
258     free(url);
259
260     return err;
261 }
262
263 /*
264  * Downloads file from url, installs in package database, return package name. 
265  */
266 int opkg_prepare_url_for_install(opkg_conf_t *conf, const char *url, char **namep)
267 {
268      int err = 0;
269      pkg_t *pkg;
270
271      pkg = pkg_new();
272
273      if (str_starts_with(url, "http://")
274          || str_starts_with(url, "ftp://")) {
275           char *tmp_file;
276           char *file_basec = xstrdup(url);
277           char *file_base = basename(file_basec);
278
279           sprintf_alloc(&tmp_file, "%s/%s", conf->tmp_dir, file_base);
280           err = opkg_download(conf, url, tmp_file, NULL, NULL);
281           if (err)
282                return err;
283
284           err = pkg_init_from_file(conf, pkg, tmp_file);
285           if (err)
286                return err;
287
288           free(tmp_file);
289           free(file_basec);
290
291      } else if (strcmp(&url[strlen(url) - 4], OPKG_PKG_EXTENSION) == 0
292                 || strcmp(&url[strlen(url) - 4], IPKG_PKG_EXTENSION) == 0
293                 || strcmp(&url[strlen(url) - 4], DPKG_PKG_EXTENSION) == 0) {
294
295           err = pkg_init_from_file(conf, pkg, url);
296           if (err)
297                return err;
298           opkg_message(conf, OPKG_DEBUG2, "Package %s provided by hand (%s).\n", pkg->name,pkg->local_filename);
299           pkg->provided_by_hand = 1;
300
301      } else {
302        pkg_deinit(pkg);
303        free(pkg);
304        return 0;
305      }
306
307      if (!pkg->architecture) {
308           opkg_message(conf, OPKG_ERROR, "Package %s has no Architecture defined.\n", pkg->name);
309           return -1;
310      }
311
312      pkg->dest = conf->default_dest;
313      pkg->state_want = SW_INSTALL;
314      pkg->state_flag |= SF_PREFER;
315      pkg = hash_insert_pkg(&conf->pkg_hash, pkg, 1,conf);  
316
317      if (namep) {
318           *namep = pkg->name;
319      }
320      return 0;
321 }
322
323 int
324 opkg_verify_file (opkg_conf_t *conf, char *text_file, char *sig_file)
325 {
326 #if defined HAVE_GPGME
327     if (conf->check_signature == 0 )
328         return 0;
329     int status = -1;
330     gpgme_ctx_t ctx;
331     gpgme_data_t sig, text, key;
332     gpgme_error_t err = -1;
333     gpgme_verify_result_t result;
334     gpgme_signature_t s;
335     char *trusted_path = NULL;
336     
337     err = gpgme_new (&ctx);
338
339     if (err)
340         return -1;
341
342     sprintf_alloc(&trusted_path, "%s/%s", conf->offline_root, "/etc/opkg/trusted.gpg");
343     err = gpgme_data_new_from_file (&key, trusted_path, 1); 
344     free (trusted_path);
345     if (err)
346     {
347       return -1;
348     }
349     err = gpgme_op_import (ctx, key);
350     if (err)
351     {
352       gpgme_data_release (key);
353       return -1;
354     }
355     gpgme_data_release (key);
356
357     err = gpgme_data_new_from_file (&sig, sig_file, 1); 
358     if (err)
359     {
360         gpgme_release (ctx);
361         return -1;
362     }
363
364     err = gpgme_data_new_from_file (&text, text_file, 1); 
365     if (err)
366     {
367         gpgme_data_release (sig);
368         gpgme_release (ctx);
369         return -1;
370     }
371
372     err = gpgme_op_verify (ctx, sig, text, NULL);
373
374     result = gpgme_op_verify_result (ctx);
375     if (!result)
376         return -1;
377
378     /* see if any of the signitures matched */
379     s = result->signatures;
380     while (s)
381     {
382         status = gpg_err_code (s->status);
383         if (status == GPG_ERR_NO_ERROR)
384             break;
385         s = s->next;
386     }
387
388
389     gpgme_data_release (sig);
390     gpgme_data_release (text);
391     gpgme_release (ctx);
392
393     return status;
394 #elif defined HAVE_OPENSSL
395     X509_STORE *store = NULL;
396     PKCS7 *p7 = NULL;
397     BIO *in = NULL, *indata = NULL;
398
399     // Sig check failed by default !
400     int status = -1;
401
402     openssl_init();
403
404     // Set-up the key store
405     if(!(store = setup_verify(conf, conf->signature_ca_file, conf->signature_ca_path))){
406         opkg_message(conf, OPKG_ERROR,
407                 "Can't open CA certificates\n");
408         goto verify_file_end;
409     }
410
411     // Open a BIO to read the sig file
412     if (!(in = BIO_new_file(sig_file, "rb"))){
413         opkg_message(conf, OPKG_ERROR,
414                 "Can't open signature file %s\n", sig_file);
415         goto verify_file_end;
416     }
417
418     // Read the PKCS7 block contained in the sig file
419     p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL);
420     if(!p7){
421         opkg_message(conf, OPKG_ERROR,
422                 "Can't read signature file (Corrupted ?)\n");
423         goto verify_file_end;
424     }
425 #if defined(HAVE_PATHFINDER)
426     if(conf->check_x509_path){
427         if(!pkcs7_pathfinder_verify_signers(p7)){
428             opkg_message(conf,  OPKG_ERROR, "pkcs7_pathfinder_verify_signers: "
429                     "Path verification failed\n");
430             goto verify_file_end;
431         }
432     }
433 #endif
434
435     // Open the Package file to authenticate
436     if (!(indata = BIO_new_file(text_file, "rb"))){
437         opkg_message(conf, OPKG_ERROR,
438                 "Can't open file %s\n", text_file);
439         goto verify_file_end;
440     }
441
442     // Let's verify the autenticity !
443     if (PKCS7_verify(p7, NULL, store, indata, NULL, PKCS7_BINARY) != 1){
444         // Get Off My Lawn!
445         opkg_message(conf, OPKG_ERROR,
446                 "Verification failure\n");
447     }else{
448         // Victory !
449         status = 0;
450     }
451
452 verify_file_end:
453     BIO_free(in);
454     BIO_free(indata);
455     PKCS7_free(p7);
456     X509_STORE_free(store);
457
458     return status;
459 #else
460     /* mute `unused variable' warnings. */
461     (void) sig_file;
462     (void) text_file;
463     (void) conf;
464     return 0;
465 #endif
466 }
467
468
469 #if defined(HAVE_OPENSSL) || defined(HAVE_SSLCURL)
470 static void openssl_init(void){
471     static int init = 0;
472
473     if(!init){
474         OPENSSL_config(NULL);
475         OpenSSL_add_all_algorithms();
476         ERR_load_crypto_strings();
477         init = 1;
478     }
479 }
480
481 #endif
482
483
484 #if defined HAVE_OPENSSL
485 static X509_STORE *setup_verify(opkg_conf_t *conf, char *CAfile, char *CApath){
486     X509_STORE *store = NULL;
487     X509_LOOKUP *lookup = NULL;
488
489     if(!(store = X509_STORE_new())){
490         // Something bad is happening...
491         goto end;
492     }
493
494     // adds the X509 file lookup method
495     lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file());
496     if (lookup == NULL){
497         goto end;
498     }
499
500     // Autenticating against one CA file
501     if (CAfile) {
502         if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
503             // Invalid CA => Bye bye
504             opkg_message(conf, OPKG_ERROR,
505                     "Error loading file %s\n", CAfile);
506             goto end;
507         }
508     } else {
509         X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
510     }
511
512     // Now look into CApath directory if supplied
513     lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
514     if (lookup == NULL){
515         goto end;
516     }
517
518     if (CApath) {
519         if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
520             opkg_message(conf, OPKG_ERROR,
521                     "Error loading directory %s\n", CApath);
522             goto end;
523         }
524     } else {
525         X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
526     }
527
528     // All right !
529     ERR_clear_error();
530     return store;
531
532 end:
533
534     X509_STORE_free(store);
535     return NULL;
536
537 }
538
539 #endif
540
541 #ifdef HAVE_CURL
542 void opkg_curl_cleanup(void){
543     if(curl != NULL){
544         curl_easy_cleanup (curl);
545         curl = NULL;
546     }
547 }
548
549 static CURL *opkg_curl_init(opkg_conf_t *conf, curl_progress_func cb, void *data){
550
551     if(curl == NULL){
552         curl = curl_easy_init();
553
554 #ifdef HAVE_SSLCURL
555         openssl_init();
556
557         if (conf->ssl_engine) {
558
559             /* use crypto engine */
560             if (curl_easy_setopt(curl, CURLOPT_SSLENGINE, conf->ssl_engine) != CURLE_OK){
561                 opkg_message(conf, OPKG_ERROR, "can't set crypto engine: '%s'\n",
562                         conf->ssl_engine);
563
564                 opkg_curl_cleanup();
565                 return NULL;
566             }
567             /* set the crypto engine as default */
568             if (curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK){
569                 opkg_message(conf, OPKG_ERROR, "can't set crypto engine as default\n");
570
571                 opkg_curl_cleanup();
572                 return NULL;
573             }
574         }
575
576         /* cert & key can only be in PEM case in the same file */
577         if(conf->ssl_key_passwd){
578             if (curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, conf->ssl_key_passwd) != CURLE_OK)
579             {
580                 opkg_message(conf, OPKG_DEBUG, "Failed to set key password\n");
581             }
582         }
583
584         /* sets the client certificate and its type */
585         if(conf->ssl_cert_type){
586             if (curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, conf->ssl_cert_type) != CURLE_OK)
587             {
588                 opkg_message(conf, OPKG_DEBUG, "Failed to set certificate format\n");
589             }
590         }
591         /* SSL cert name isn't mandatory */
592         if(conf->ssl_cert){
593                 curl_easy_setopt(curl, CURLOPT_SSLCERT, conf->ssl_cert);
594         }
595
596         /* sets the client key and its type */
597         if(conf->ssl_key_type){
598             if (curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, conf->ssl_key_type) != CURLE_OK)
599             {
600                 opkg_message(conf, OPKG_DEBUG, "Failed to set key format\n");
601             }
602         }
603         if(conf->ssl_key){
604             if (curl_easy_setopt(curl, CURLOPT_SSLKEY, conf->ssl_key) != CURLE_OK)
605             {
606                 opkg_message(conf, OPKG_DEBUG, "Failed to set key\n");
607             }
608         }
609
610         /* Should we verify the peer certificate ? */
611         if(conf->ssl_dont_verify_peer){
612             /*
613              * CURLOPT_SSL_VERIFYPEER default is nonzero (curl => 7.10)
614              */
615             curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
616         }else{
617 #ifdef HAVE_PATHFINDER
618             if(conf->check_x509_path){
619                 if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_ssl_ctx_function) != CURLE_OK){
620                     opkg_message(conf, OPKG_DEBUG, "Failed to set ssl path verification callback\n");
621                 }else{
622                     curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, NULL);
623                 }
624             }
625 #endif
626         }
627
628         /* certification authority file and/or path */
629         if(conf->ssl_ca_file){
630             curl_easy_setopt(curl, CURLOPT_CAINFO, conf->ssl_ca_file);
631         }
632         if(conf->ssl_ca_path){
633             curl_easy_setopt(curl, CURLOPT_CAPATH, conf->ssl_ca_path);
634         }
635 #endif
636
637         curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
638         curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
639         if (conf->http_proxy || conf->ftp_proxy)
640         {
641             char *userpwd;
642             sprintf_alloc (&userpwd, "%s:%s", conf->proxy_user,
643                     conf->proxy_passwd);
644             curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd);
645             free (userpwd);
646         }
647     }
648
649     curl_easy_setopt (curl, CURLOPT_NOPROGRESS, (cb == NULL));
650     if (cb)
651     {
652         curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, data);
653         curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, cb);
654     }
655
656     return curl;
657
658 }
659 #endif