Removed a unused variable warning
[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 #ifdef HAVE_CURL
21 #include <curl/curl.h>
22 #endif
23 #if defined(HAVE_GPGME)
24 #include <gpgme.h>
25 #elif defined(HAVE_OPENSSL)
26 #include <openssl/bio.h>
27 #include <openssl/err.h>
28 #include <openssl/evp.h>
29 #include <openssl/objects.h>
30 #include <openssl/x509.h>
31 #include <openssl/pem.h>
32 #include <openssl/hmac.h>
33
34 #endif
35
36 #include "includes.h"
37 #include "opkg_download.h"
38 #include "opkg_message.h"
39 #include "opkg_state.h"
40
41 #include "sprintf_alloc.h"
42 #include "xsystem.h"
43 #include "file_util.h"
44 #include "str_util.h"
45 #include "opkg_defines.h"
46
47
48 #ifdef HAVE_OPENSSL
49 static X509_STORE *setup_verify(opkg_conf_t *conf, char *CAfile, char *CApath);
50 static void init_openssl(void);
51 #endif
52
53 int opkg_download(opkg_conf_t *conf, const char *src,
54   const char *dest_file_name, curl_progress_func cb, void *data)
55 {
56     int err = 0;
57
58     char *src_basec = strdup(src);
59     char *src_base = basename(src_basec);
60     char *tmp_file_location;
61
62     opkg_message(conf,OPKG_NOTICE,"Downloading %s\n", src);
63         
64     if (str_starts_with(src, "file:")) {
65         int ret;
66         const char *file_src = src + 5;
67         opkg_message(conf,OPKG_INFO,"Copying %s to %s...", file_src, dest_file_name);
68         ret = file_copy(src + 5, dest_file_name);
69         opkg_message(conf,OPKG_INFO,"Done\n");
70         free(src_basec);
71         return ret;
72     }
73
74     sprintf_alloc(&tmp_file_location, "%s/%s", conf->tmp_dir, src_base);
75     err = unlink(tmp_file_location);
76     if (err && errno != ENOENT) {
77         opkg_message(conf,OPKG_ERROR, "%s: ERROR: failed to unlink %s: %s\n",
78                 __FUNCTION__, tmp_file_location, strerror(errno));
79         free(tmp_file_location);
80         free(src_basec);
81         return errno;
82     }
83
84     if (conf->http_proxy) {
85         opkg_message(conf,OPKG_DEBUG,"Setting environment variable: http_proxy = %s\n", conf->http_proxy);
86         setenv("http_proxy", conf->http_proxy, 1);
87     }
88     if (conf->ftp_proxy) {
89         opkg_message(conf,OPKG_DEBUG,"Setting environment variable: ftp_proxy = %s\n", conf->ftp_proxy);
90         setenv("ftp_proxy", conf->ftp_proxy, 1);
91     }
92     if (conf->no_proxy) {
93         opkg_message(conf,OPKG_DEBUG,"Setting environment variable: no_proxy = %s\n", conf->no_proxy);
94         setenv("no_proxy", conf->no_proxy, 1);
95     }
96
97 #ifdef HAVE_CURL
98     CURL *curl;
99     CURLcode res;
100     FILE * file = fopen (tmp_file_location, "w");
101
102     curl = curl_easy_init ();
103     if (curl)
104     {
105         curl_easy_setopt (curl, CURLOPT_URL, src);
106         curl_easy_setopt (curl, CURLOPT_WRITEDATA, file);
107         curl_easy_setopt (curl, CURLOPT_NOPROGRESS, (cb == NULL));
108         if (cb)
109         {
110                 curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, data);
111                 curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, cb);
112         }
113         curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
114         curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
115         if (conf->http_proxy || conf->ftp_proxy)
116         {
117             char *userpwd;
118             sprintf_alloc (&userpwd, "%s:%s", conf->proxy_user,
119                     conf->proxy_passwd);
120             curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd);
121             free (userpwd);
122         }
123         res = curl_easy_perform (curl);
124         fclose (file);
125         if (res)
126         {
127             long error_code;
128             curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &error_code);
129             opkg_message(conf, OPKG_ERROR, "Failed to download %s. \nerror detail: %s\n", src, curl_easy_strerror(res));
130             free(tmp_file_location);
131             free(src_basec);
132             curl_easy_cleanup (curl);
133             return res;
134         }
135         curl_easy_cleanup (curl);
136
137     }
138     else
139     {
140         free(tmp_file_location);
141         free(src_basec);
142         return -1;
143     }
144 #else
145     {
146       int res;
147       char *wgetcmd;
148       char *wgetopts;
149       wgetopts = getenv("OPKG_WGETOPTS");
150       sprintf_alloc(&wgetcmd, "wget -q %s%s -O \"%s\" \"%s\"",
151                     (conf->http_proxy || conf->ftp_proxy) ? "-Y on " : "",
152                     (wgetopts!=NULL) ? wgetopts : "",
153                     tmp_file_location, src);
154       opkg_message(conf, OPKG_INFO, "Executing: %s\n", wgetcmd);
155       res = xsystem(wgetcmd);
156       free(wgetcmd);
157       if (res) {
158         opkg_message(conf, OPKG_ERROR, "Failed to download %s, error %d\n", src, res);
159         free(tmp_file_location);
160         free(src_basec);
161         return res;
162       }
163     }
164 #endif
165
166     err = file_move(tmp_file_location, dest_file_name);
167
168     free(tmp_file_location);
169     free(src_basec);
170
171     if (err) {
172         return err;
173     }
174
175     return 0;
176 }
177
178 static int opkg_download_cache(opkg_conf_t *conf, const char *src,
179   const char *dest_file_name, curl_progress_func cb, void *data)
180 {
181     char *cache_name = strdup(src);
182     char *cache_location, *p;
183     int err = 0;
184
185     if (!conf->cache || str_starts_with(src, "file:")) {
186         err = opkg_download(conf, src, dest_file_name, cb, data);
187         goto out1;
188     }
189
190     for (p = cache_name; *p; p++)
191         if (*p == '/')
192             *p = ',';   /* looks nicer than | or # */
193
194     sprintf_alloc(&cache_location, "%s/%s", conf->cache, cache_name);
195     if (file_exists(cache_location))
196         opkg_message(conf, OPKG_NOTICE, "Copying %s\n", cache_location);
197     else {
198         err = opkg_download(conf, src, cache_location, cb, data);
199         if (err) {
200             (void) unlink(cache_location);
201             goto out2;
202         }
203     }
204
205     err = file_copy(cache_location, dest_file_name);
206
207
208 out2:
209     free(cache_location);
210 out1:
211     free(cache_name);
212     return err;
213 }
214
215 int opkg_download_pkg(opkg_conf_t *conf, pkg_t *pkg, const char *dir)
216 {
217     int err;
218     char *url;
219     char *pkgid;
220     char *stripped_filename;
221
222     if (pkg->src == NULL) {
223         opkg_message(conf,OPKG_ERROR, "ERROR: Package %s (parent %s) is not available from any configured src.\n",
224                 pkg->name, pkg->parent->name);
225         return -1;
226     }
227     if (pkg->filename == NULL) {
228         opkg_message(conf,OPKG_ERROR, "ERROR: Package %s (parent %s) does not have a valid filename field.\n",pkg->name, pkg->parent->name);
229         return -1;
230     }
231
232     sprintf_alloc (&pkgid, "%s;%s;%s;", pkg->name, pkg->version, pkg->architecture);
233     opkg_set_current_state (conf, OPKG_STATE_DOWNLOADING_PKG, pkgid);
234     free (pkgid);
235
236     sprintf_alloc(&url, "%s/%s", pkg->src->value, pkg->filename);
237
238     /* XXX: BUG: The pkg->filename might be something like
239        "../../foo.opk". While this is correct, and exactly what we
240        want to use to construct url above, here we actually need to
241        use just the filename part, without any directory. */
242
243     stripped_filename = strrchr(pkg->filename, '/');
244     if ( ! stripped_filename )
245         stripped_filename = pkg->filename;
246
247     sprintf_alloc(&pkg->local_filename, "%s/%s", dir, stripped_filename);
248
249     err = opkg_download_cache(conf, url, pkg->local_filename, NULL, NULL);
250     free(url);
251
252     opkg_set_current_state (conf, OPKG_STATE_NONE, NULL);
253     return err;
254 }
255
256 /*
257  * Downloads file from url, installs in package database, return package name. 
258  */
259 int opkg_prepare_url_for_install(opkg_conf_t *conf, const char *url, char **namep)
260 {
261      int err = 0;
262      pkg_t *pkg;
263      pkg = pkg_new();
264      if (pkg == NULL)
265           return ENOMEM;
266
267      if (str_starts_with(url, "http://")
268          || str_starts_with(url, "ftp://")) {
269           char *tmp_file;
270           char *file_basec = strdup(url);
271           char *file_base = basename(file_basec);
272
273           sprintf_alloc(&tmp_file, "%s/%s", conf->tmp_dir, file_base);
274           err = opkg_download(conf, url, tmp_file, NULL, NULL);
275           if (err)
276                return err;
277
278           err = pkg_init_from_file(pkg, tmp_file);
279           if (err)
280                return err;
281           pkg->local_filename = strdup(tmp_file);
282
283           free(tmp_file);
284           free(file_basec);
285
286      } else if (strcmp(&url[strlen(url) - 4], OPKG_PKG_EXTENSION) == 0
287                 || strcmp(&url[strlen(url) - 4], IPKG_PKG_EXTENSION) == 0
288                 || strcmp(&url[strlen(url) - 4], DPKG_PKG_EXTENSION) == 0) {
289
290           err = pkg_init_from_file(pkg, url);
291           if (err)
292                return err;
293           pkg->local_filename = strdup(url);
294           opkg_message(conf, OPKG_DEBUG2, "Package %s provided by hand (%s).\n", pkg->name,pkg->local_filename);
295           pkg->provided_by_hand = 1;
296
297      } else {
298        pkg_deinit(pkg);
299        free(pkg);
300        return 0;
301      }
302
303      if (!pkg->architecture) {
304           opkg_message(conf, OPKG_ERROR, "Package %s has no Architecture defined.\n", pkg->name);
305           return -EINVAL;
306      }
307
308      pkg->dest = conf->default_dest;
309      pkg->state_want = SW_INSTALL;
310      pkg->state_flag |= SF_PREFER;
311      pkg = hash_insert_pkg(&conf->pkg_hash, pkg, 1,conf);  
312      if ( pkg == NULL ){
313         fprintf(stderr, "%s : This should never happen. Report this Bug in bugzilla please \n ",__FUNCTION__);
314         return 0;
315      }
316      if (namep) {
317           *namep = strdup(pkg->name);
318      }
319      return 0;
320 }
321
322 int
323 opkg_verify_file (opkg_conf_t *conf, char *text_file, char *sig_file)
324 {
325 #if defined HAVE_GPGME
326     if (conf->check_signature == 0 )
327         return 0;
328     int status = -1;
329     gpgme_ctx_t ctx;
330     gpgme_data_t sig, text, key;
331     gpgme_error_t err = -1;
332     gpgme_verify_result_t result;
333     gpgme_signature_t s;
334     char *trusted_path = NULL;
335     
336     err = gpgme_new (&ctx);
337
338     if (err)
339         return -1;
340
341     sprintf_alloc(&trusted_path, "%s/%s", conf->offline_root, "/etc/opkg/trusted.gpg");
342     err = gpgme_data_new_from_file (&key, trusted_path, 1); 
343     free (trusted_path);
344     if (err)
345     {
346       return -1;
347     }
348     err = gpgme_op_import (ctx, key);
349     if (err)
350     {
351       gpgme_data_release (key);
352       return -1;
353     }
354     gpgme_data_release (key);
355
356     err = gpgme_data_new_from_file (&sig, sig_file, 1); 
357     if (err)
358     {
359         gpgme_release (ctx);
360         return -1;
361     }
362
363     err = gpgme_data_new_from_file (&text, text_file, 1); 
364     if (err)
365     {
366         gpgme_data_release (sig);
367         gpgme_release (ctx);
368         return -1;
369     }
370
371     err = gpgme_op_verify (ctx, sig, text, NULL);
372
373     result = gpgme_op_verify_result (ctx);
374     if (!result)
375         return -1;
376
377     /* see if any of the signitures matched */
378     s = result->signatures;
379     while (s)
380     {
381         status = gpg_err_code (s->status);
382         if (status == GPG_ERR_NO_ERROR)
383             break;
384         s = s->next;
385     }
386
387
388     gpgme_data_release (sig);
389     gpgme_data_release (text);
390     gpgme_release (ctx);
391
392     return status;
393 #elif defined HAVE_OPENSSL
394     X509_STORE *store = NULL;
395     PKCS7 *p7 = NULL;
396     BIO *in = NULL, *indata = NULL;
397
398     // Sig check failed by default !
399     int status = -1;
400
401     init_openssl();
402
403     // Set-up the key store
404     if(!(store = setup_verify(conf, conf->signature_ca_file, conf->signature_ca_path))){
405         opkg_message(conf, OPKG_ERROR,
406                 "Can't open CA certificates\n");
407         goto verify_file_end;
408     }
409
410     // Open a BIO to read the sig file
411     if (!(in = BIO_new_file(sig_file, "rb"))){
412         opkg_message(conf, OPKG_ERROR,
413                 "Can't open signature file %s\n", sig_file);
414         goto verify_file_end;
415     }
416
417     // Read the PKCS7 block contained in the sig file
418     p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL);
419     if(!p7){
420         opkg_message(conf, OPKG_ERROR,
421                 "Can't read signature file (Corrupted ?)\n");
422         goto verify_file_end;
423     }
424
425     // Open the Package file to authenticate
426     if (!(indata = BIO_new_file(text_file, "rb"))){
427         opkg_message(conf, OPKG_ERROR,
428                 "Can't open file %s\n", text_file);
429         goto verify_file_end;
430     }
431
432     // Let's verify the autenticity !
433     if (PKCS7_verify(p7, NULL, store, indata, NULL, PKCS7_BINARY) != 1){
434         // Get Off My Lawn!
435         opkg_message(conf, OPKG_ERROR,
436                 "Verification failure\n");
437     }else{
438         // Victory !
439         status = 0;
440     }
441
442 verify_file_end:
443     BIO_free(in);
444     BIO_free(indata);
445     PKCS7_free(p7);
446     X509_STORE_free(store);
447
448     return status;
449 #else
450     /* mute `unused variable' warnings. */
451     (void) sig_file;
452     (void) text_file;
453     (void) conf;
454     return 0;
455 #endif
456 }
457
458
459 #if defined HAVE_OPENSSL
460 static X509_STORE *setup_verify(opkg_conf_t *conf, char *CAfile, char *CApath){
461     X509_STORE *store = NULL;
462     X509_LOOKUP *lookup = NULL;
463
464     if(!(store = X509_STORE_new())){
465         // Something bad is happening...
466         goto end;
467     }
468
469     // adds the X509 file lookup method
470     lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file());
471     if (lookup == NULL){
472         goto end;
473     }
474
475     // Autenticating against one CA file
476     if (CAfile) {
477         if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
478             // Invalid CA => Bye bye
479             opkg_message(conf, OPKG_ERROR,
480                     "Error loading file %s\n", CAfile);
481             goto end;
482         }
483     } else {
484         X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
485     }
486
487     // Now look into CApath directory if supplied
488     lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
489     if (lookup == NULL){
490         goto end;
491     }
492
493     if (CApath) {
494         if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
495             opkg_message(conf, OPKG_ERROR,
496                     "Error loading directory %s\n", CApath);
497             goto end;
498         }
499     } else {
500         X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
501     }
502
503     // All right !
504     ERR_clear_error();
505     return store;
506
507 end:
508
509     X509_STORE_free(store);
510     return NULL;
511
512 }
513
514 static void init_openssl(void){
515     static int init = 0;
516
517     if(!init){      
518         OpenSSL_add_all_algorithms();       
519         ERR_load_crypto_strings();
520         init = 1;
521     }
522 }
523 #endif