Make `curl' an instance variable and ssl support
authorpixdamix <pixdamix@e8e0d7a0-c8d9-11dd-a880-a1081c7ac358>
Tue, 3 Nov 2009 09:27:03 +0000 (09:27 +0000)
committerpixdamix <pixdamix@e8e0d7a0-c8d9-11dd-a880-a1081c7ac358>
Tue, 3 Nov 2009 09:27:03 +0000 (09:27 +0000)
This patch adds several new options

  - option ssl_ca_path /path/to/dir
     Tells curl to use the specified certificate directory to verify the
     peer. The certificates must be in PEM format, and the directory
     must have been processed using the c_rehash utility supplied with
     openssl.

  - option ssl_ca_file
     Tells curl to use the specified certificate file to verify the
     peer. The file may contain multiple CA certificates

  - option ssl_key_type
     Tells curl the Private key file type. Specify which type your
     ssl_key provided private key is. PEM (default), DER and ENG
     (see option ssl_engine) are recognized types.

  - option ssl_cert_type
     Tells curl what certificate type the provided certificate is in.
     PEM (default), DER and ENG (see option ssl_engine) are recognized
     types.

  - option ssl_key & option ssl_cert
     Tells curl to use the specified certificate file and private key
     when getting a file with HTTPS

  - option ssl_key_passwd
     Passphrase for the private key

  - option ssl_engine
     Select the OpenSSL crypto engine to use for cipher operations.

  - option ssl_dont_verify_peer
     This option explicitly allows curl to perform "insecure" SSL
     connections and transfers. All SSL connections are attempted to be
     made secure by using the CA certificate. This makes all connections
     considered "insecure" fail unless ssl_dont_verify_peer is used.

git-svn-id: http://opkg.googlecode.com/svn/trunk@251 e8e0d7a0-c8d9-11dd-a880-a1081c7ac358

configure.ac
libopkg/Makefile.am
libopkg/opkg_conf.c
libopkg/opkg_conf.h
libopkg/opkg_download.c

index 579a105..95233b5 100644 (file)
@@ -15,6 +15,9 @@ for top_builddir in . .. ../.. $ac_auxdir $ac_auxdir/..; do
   test -f $top_builddir/configure && break
 done
 
+# large file support can be useful for gpgme
+AC_SYS_LARGEFILE
+
 
 # Checks for programs
 AC_PROG_AWK
@@ -55,8 +58,44 @@ AC_ARG_ENABLE(openssl,
     [want_openssl="$enableval"], [want_openssl="no"])
 
 if test "x$want_openssl" = "xyes"; then
-  PKG_CHECK_MODULES(OPENSSL, openssl)
   AC_DEFINE(HAVE_OPENSSL, 1, [Define if you want OpenSSL support])
+  NEED_SSL_LIBS="yes"
+fi
+
+# check for libssl-curl
+AC_ARG_ENABLE(ssl-curl,
+              AC_HELP_STRING([--enable-ssl-curl], [Enable certificate authentication with curl
+                              [[default="$default_sslcurl"]] ]),
+                              [want_sslcurl="$enableval"], [want_sslcurl="yes"])
+
+if test "x$want_curl" = "xyes" -a "x$want_sslcurl" = "xyes"; then
+  AC_DEFINE(HAVE_CURL, 1, [Define if you want CURL support])
+  AC_DEFINE(HAVE_SSLCURL, 1, [Define if you want certificate authentication with curl])
+  NEED_SSL_LIBS="yes"
+fi
+
+if test "x$NEED_SSL_LIBS" = "xyes"; then
+  AC_MSG_CHECKING([if openssl is available])
+
+  PKG_CHECK_MODULES(OPENSSL, openssl, [:], [:])
+  if test "x$OPENSSL_LIBS" != "x"; then
+          AC_MSG_RESULT(yes)
+  else
+          OPENSSL_LIBS="-lcrypto -lssl"
+          dnl If pkg-config fails, run compile/link test.
+          AC_TRY_LINK([
+#include <openssl/opensslv.h>
+], [
+return OPENSSL_VERSION_NUMBER; ],
+          [
+            AC_MSG_RESULT(yes)
+
+          ], [
+            AC_MSG_RESULT(no)
+            AC_MSG_ERROR(OpenSSL not found)
+          ])
+  fi
+  AC_SUBST(OPENSSL_LIBS)
 fi
 
 
index 28fdeb9..555b0e5 100644 (file)
@@ -40,7 +40,7 @@ libopkg_la_SOURCES = \
        $(opkg_cmd_sources) $(opkg_db_sources) \
        $(opkg_util_sources) $(opkg_list_sources)
 
-libopkg_la_LIBADD = $(top_builddir)/libbb/libbb.la $(CURL_LIBS) $(GPGME_LIBS)
+libopkg_la_LIBADD = $(top_builddir)/libbb/libbb.la $(CURL_LIBS) $(GPGME_LIBS) $(OPENSSL_LIBS)
 # make sure we only export symbols that are for public use
 libopkg_la_LDFLAGS = -export-symbols-regex "^opkg_.*"
 
index 2739861..4ea15d6 100644 (file)
@@ -78,6 +78,17 @@ int opkg_init_options_array(const opkg_conf_t *conf, opkg_option_t **options)
 #if defined(HAVE_OPENSSL)
          { "signature_ca_file", OPKG_OPT_TYPE_STRING, &conf->signature_ca_file },
          { "signature_ca_path", OPKG_OPT_TYPE_STRING, &conf->signature_ca_path },
+#endif
+#if defined(HAVE_SSLCURL) && defined(HAVE_CURL)
+          { "ssl_engine", OPKG_OPT_TYPE_STRING, &conf->ssl_engine },
+          { "ssl_cert", OPKG_OPT_TYPE_STRING, &conf->ssl_cert },
+          { "ssl_cert_type", OPKG_OPT_TYPE_STRING, &conf->ssl_cert_type },
+          { "ssl_key", OPKG_OPT_TYPE_STRING, &conf->ssl_key },
+          { "ssl_key_type", OPKG_OPT_TYPE_STRING, &conf->ssl_key_type },
+          { "ssl_key_passwd", OPKG_OPT_TYPE_STRING, &conf->ssl_key_passwd },
+          { "ssl_ca_file", OPKG_OPT_TYPE_STRING, &conf->ssl_ca_file },
+          { "ssl_ca_path", OPKG_OPT_TYPE_STRING, &conf->ssl_ca_path },
+          { "ssl_dont_verify_peer", OPKG_OPT_TYPE_BOOL, &conf->ssl_dont_verify_peer },
 #endif
          { NULL }
      };
@@ -376,6 +387,17 @@ void opkg_conf_deinit(opkg_conf_t *conf)
      opkg_conf_free_string(&conf->signature_ca_path);
 #endif
 
+#if defined(HAVE_SSLCURL)
+     opkg_conf_free_string(&conf->ssl_engine);
+     opkg_conf_free_string(&conf->ssl_cert);
+     opkg_conf_free_string(&conf->ssl_cert_type);
+     opkg_conf_free_string(&conf->ssl_key);
+     opkg_conf_free_string(&conf->ssl_key_type);
+     opkg_conf_free_string(&conf->ssl_key_passwd);
+     opkg_conf_free_string(&conf->ssl_ca_file);
+     opkg_conf_free_string(&conf->ssl_ca_path);
+#endif
+
      if (conf->verbosity > 1) { 
          int i;
          hash_table_t *hashes[] = {
index 4bd50e5..ecfe9ea 100644 (file)
@@ -75,6 +75,22 @@ struct opkg_conf
      int noaction;
      char *cache;
 
+#ifdef HAVE_SSLCURL
+     /* some options could be used by
+      * wget if curl support isn't builtin
+      * If someone want to try...
+      */
+     char *ssl_engine;
+     char *ssl_cert;
+     char *ssl_cert_type;
+     char *ssl_key;
+     char *ssl_key_type;
+     char *ssl_key_passwd;
+     char *ssl_ca_file;
+     char *ssl_ca_path;
+     int ssl_dont_verify_peer;
+#endif
+
      /* proxy options */
      char *http_proxy;
      char *ftp_proxy;
index ee8dc08..33019d8 100644 (file)
    General Public License for more details.
 */
 #include "config.h"
+
 #ifdef HAVE_CURL
 #include <curl/curl.h>
 #endif
+
+#if defined(HAVE_SSLCURL) || defined(HAVE_OPENSSL)
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#endif
+
 #if defined(HAVE_GPGME)
 #include <gpgme.h>
 #elif defined(HAVE_OPENSSL)
 #include <openssl/bio.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
 #include <openssl/objects.h>
 #include <openssl/x509.h>
 #include <openssl/pem.h>
 #include <openssl/hmac.h>
-
 #endif
 
 #include "includes.h"
 #include "str_util.h"
 #include "opkg_defines.h"
 
+#if defined(HAVE_OPENSSL) || defined(HAVE_SSLCURL)
+static void openssl_init(void);
+#endif
 
 #ifdef HAVE_OPENSSL
 static X509_STORE *setup_verify(opkg_conf_t *conf, char *CAfile, char *CApath);
-static void init_openssl(void);
+#endif
+
+#ifdef HAVE_CURL
+/*
+ * Make curl an instance variable so we don't have to instanciate it
+ * each time
+ */
+static CURL *curl = NULL;
+static void opkg_curl_cleanup(void);
+static CURL *opkg_curl_init(opkg_conf_t *conf, curl_progress_func cb, void *data);
 #endif
 
 int opkg_download(opkg_conf_t *conf, const char *src,
@@ -99,27 +116,12 @@ int opkg_download(opkg_conf_t *conf, const char *src,
     CURLcode res;
     FILE * file = fopen (tmp_file_location, "w");
 
-    curl = curl_easy_init ();
+    curl = opkg_curl_init (conf, cb, data);
     if (curl)
     {
        curl_easy_setopt (curl, CURLOPT_URL, src);
        curl_easy_setopt (curl, CURLOPT_WRITEDATA, file);
-       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, (cb == NULL));
-       if (cb)
-       {
-               curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, data);
-               curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, cb);
-       }
-       curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
-       curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
-       if (conf->http_proxy || conf->ftp_proxy)
-       {
-           char *userpwd;
-           sprintf_alloc (&userpwd, "%s:%s", conf->proxy_user,
-                   conf->proxy_passwd);
-           curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd);
-           free (userpwd);
-       }
+
        res = curl_easy_perform (curl);
        fclose (file);
        if (res)
@@ -129,10 +131,8 @@ int opkg_download(opkg_conf_t *conf, const char *src,
            opkg_message(conf, OPKG_ERROR, "Failed to download %s. \nerror detail: %s\n", src, curl_easy_strerror(res));
            free(tmp_file_location);
             free(src_basec);
-           curl_easy_cleanup (curl);
            return res;
        }
-       curl_easy_cleanup (curl);
 
     }
     else
@@ -396,7 +396,7 @@ opkg_verify_file (opkg_conf_t *conf, char *text_file, char *sig_file)
     // Sig check failed by default !
     int status = -1;
 
-    init_openssl();
+    openssl_init();
 
     // Set-up the key store
     if(!(store = setup_verify(conf, conf->signature_ca_file, conf->signature_ca_path))){
@@ -454,6 +454,21 @@ verify_file_end:
 }
 
 
+#if defined(HAVE_OPENSSL) || defined(HAVE_SSLCURL)
+static void openssl_init(void){
+    static int init = 0;
+
+    if(!init){
+       OPENSSL_config(NULL);
+        OpenSSL_add_all_algorithms();
+        ERR_load_crypto_strings();
+        init = 1;
+    }
+}
+
+#endif
+
+
 #if defined HAVE_OPENSSL
 static X509_STORE *setup_verify(opkg_conf_t *conf, char *CAfile, char *CApath){
     X509_STORE *store = NULL;
@@ -509,13 +524,120 @@ end:
 
 }
 
-static void init_openssl(void){
-    static int init = 0;
+#endif
+
+#ifdef HAVE_CURL
+static void opkg_curl_cleanup(void){
+    if(curl != NULL){
+       curl_easy_cleanup (curl);
+       curl = NULL;
+    }
+}
+
+static CURL *opkg_curl_init(opkg_conf_t *conf, curl_progress_func cb, void *data){
+
+    if(curl == NULL){
+       curl = curl_easy_init();
+
+#ifdef HAVE_SSLCURL
+       openssl_init();
+
+       if (conf->ssl_engine) {
+
+           /* use crypto engine */
+           if (curl_easy_setopt(curl, CURLOPT_SSLENGINE, conf->ssl_engine) != CURLE_OK){
+               opkg_message(conf, OPKG_ERROR, "can't set crypto engine: '%s'\n",
+                       conf->ssl_engine);
+
+               opkg_curl_cleanup();
+               return NULL;
+           }
+           /* set the crypto engine as default */
+           if (curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK){
+               opkg_message(conf, OPKG_ERROR, "can't set crypto engine as default\n");
+
+               opkg_curl_cleanup();
+               return NULL;
+           }
+       }
+
+       /* cert & key can only be in PEM case in the same file */
+       if(conf->ssl_key_passwd){
+           if (curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, conf->ssl_key_passwd) != CURLE_OK)
+           {
+               opkg_message(conf, OPKG_DEBUG, "Failed to set key password\n");
+           }
+       }
+
+       /* sets the client certificate and its type */
+       if(conf->ssl_cert_type){
+           if (curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, conf->ssl_cert_type) != CURLE_OK)
+           {
+               opkg_message(conf, OPKG_DEBUG, "Failed to set certificate format\n");
+           }
+       }
+       /* SSL cert name isn't mandatory */
+       if(conf->ssl_cert){
+               curl_easy_setopt(curl, CURLOPT_SSLCERT, conf->ssl_cert);
+       }
+
+       /* sets the client key and its type */
+       if(conf->ssl_key_type){
+           if (curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, conf->ssl_key_type) != CURLE_OK)
+           {
+               opkg_message(conf, OPKG_DEBUG, "Failed to set key format\n");
+           }
+       }
+       if(conf->ssl_key){
+           if (curl_easy_setopt(curl, CURLOPT_SSLKEY, conf->ssl_key) != CURLE_OK)
+           {
+               opkg_message(conf, OPKG_DEBUG, "Failed to set key\n");
+           }
+       }
+
+       /* Should we verify the peer certificate ? */
+       if(conf->ssl_dont_verify_peer){
+           /*
+            * CURLOPT_SSL_VERIFYPEER default is nonzero (curl => 7.10)
+            */
+           curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+       }
+
+       /* certification authority file and/or path */
+       if(conf->ssl_ca_file){
+           curl_easy_setopt(curl, CURLOPT_CAINFO, conf->ssl_ca_file);
+       }
+       if(conf->ssl_ca_path){
+           curl_easy_setopt(curl, CURLOPT_CAPATH, conf->ssl_ca_path);
+       }
+#endif
+
+       curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
+       curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
+       if (conf->http_proxy || conf->ftp_proxy)
+       {
+           char *userpwd;
+           sprintf_alloc (&userpwd, "%s:%s", conf->proxy_user,
+                   conf->proxy_passwd);
+           curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd);
+           free (userpwd);
+       }
+
+       /* add curl cleanup callback */
+       if(!atexit(opkg_curl_cleanup)){
+           opkg_message(conf,OPKG_DEBUG, "Failed to register atexit curl cleanup function\n");
+       }
 
-    if(!init){      
-        OpenSSL_add_all_algorithms();       
-        ERR_load_crypto_strings();
-        init = 1;
     }
+
+    curl_easy_setopt (curl, CURLOPT_NOPROGRESS, (cb == NULL));
+    if (cb)
+    {
+       curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, data);
+       curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, cb);
+    }
+
+    return curl;
+
 }
 #endif