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