From b3a54f7c2d686dbec049d3788f5096c01360c5c4 Mon Sep 17 00:00:00 2001 From: pixdamix Date: Tue, 3 Nov 2009 09:27:03 +0000 Subject: [PATCH] Make `curl' an instance variable and ssl support 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 | 41 ++++++++- libopkg/Makefile.am | 2 +- libopkg/opkg_conf.c | 22 +++++ libopkg/opkg_conf.h | 16 ++++ libopkg/opkg_download.c | 182 +++++++++++++++++++++++++++++++++------- 5 files changed, 231 insertions(+), 32 deletions(-) diff --git a/configure.ac b/configure.ac index 579a105..95233b5 100644 --- a/configure.ac +++ b/configure.ac @@ -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 +], [ +return OPENSSL_VERSION_NUMBER; ], + [ + AC_MSG_RESULT(yes) + + ], [ + AC_MSG_RESULT(no) + AC_MSG_ERROR(OpenSSL not found) + ]) + fi + AC_SUBST(OPENSSL_LIBS) fi diff --git a/libopkg/Makefile.am b/libopkg/Makefile.am index 28fdeb9..555b0e5 100644 --- a/libopkg/Makefile.am +++ b/libopkg/Makefile.am @@ -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_.*" diff --git a/libopkg/opkg_conf.c b/libopkg/opkg_conf.c index 2739861..4ea15d6 100644 --- a/libopkg/opkg_conf.c +++ b/libopkg/opkg_conf.c @@ -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[] = { diff --git a/libopkg/opkg_conf.h b/libopkg/opkg_conf.h index 4bd50e5..ecfe9ea 100644 --- a/libopkg/opkg_conf.h +++ b/libopkg/opkg_conf.h @@ -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; diff --git a/libopkg/opkg_download.c b/libopkg/opkg_download.c index ee8dc08..33019d8 100644 --- a/libopkg/opkg_download.c +++ b/libopkg/opkg_download.c @@ -17,20 +17,25 @@ General Public License for more details. */ #include "config.h" + #ifdef HAVE_CURL #include #endif + +#if defined(HAVE_SSLCURL) || defined(HAVE_OPENSSL) +#include +#include +#include +#endif + #if defined(HAVE_GPGME) #include #elif defined(HAVE_OPENSSL) #include -#include -#include #include #include #include #include - #endif #include "includes.h" @@ -44,10 +49,22 @@ #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 -- 2.25.1