/* SSL support - wrappers for SSL routines */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef CONFIG_OPENSSL #include #include #elif defined(CONFIG_GNUTLS) #include #else #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?" #endif #ifdef HAVE_LIMITS_H #include #endif #include "elinks.h" #include "intl/gettext/libintl.h" #include "main/module.h" #include "network/connection.h" #include "network/socket.h" #include "network/ssl/ssl.h" #include "util/conv.h" #include "util/error.h" #include "util/string.h" /* FIXME: As you can see, SSL is currently implemented in very, erm, * decentralized manner. */ #ifdef CONFIG_OPENSSL #ifndef PATH_MAX #define PATH_MAX 256 /* according to my /usr/include/bits/posix1_lim.h */ #endif SSL_CTX *context = NULL; static void init_openssl(struct module *module) { unsigned char f_randfile[PATH_MAX]; /* In a nutshell, on OS's without a /dev/urandom, the OpenSSL library * cannot initialize the PRNG and so every attempt to use SSL fails. * It's actually an OpenSSL FAQ, and according to them, it's up to the * application coders to seed the RNG. -- William Yodlowsky */ if (RAND_egd(RAND_file_name(f_randfile, sizeof(f_randfile))) < 0) { /* Not an EGD, so read and write to it */ if (RAND_load_file(f_randfile, -1)) RAND_write_file(f_randfile); } SSLeay_add_ssl_algorithms(); context = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_options(context, SSL_OP_ALL); SSL_CTX_set_default_verify_paths(context); } static void done_openssl(struct module *module) { if (context) SSL_CTX_free(context); } static struct option_info openssl_options[] = { INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"), "cert_verify", 0, 0, N_("Verify the peer's SSL certificate. Note that this\n" "needs extensive configuration of OpenSSL by the user.")), INIT_OPT_TREE("connection.ssl", N_("Client Certificates"), "client_cert", OPT_SORT, N_("X509 client certificate options.")), INIT_OPT_BOOL("connection.ssl.client_cert", N_("Enable"), "enable", 0, 0, N_("Enable or not the sending of X509 client certificates\n" "to servers which request them.")), INIT_OPT_STRING("connection.ssl.client_cert", N_("Certificate File"), "file", 0, "", N_("The location of a file containing the client certificate\n" "and unencrypted private key in PEM format. If unset, the\n" "file pointed to by the X509_CLIENT_CERT variable is used\n" "instead.")), NULL_OPTION_INFO, }; static struct module openssl_module = struct_module( /* name: */ "OpenSSL", /* options: */ openssl_options, /* events: */ NULL, /* submodules: */ NULL, /* data: */ NULL, /* init: */ init_openssl, /* done: */ done_openssl ); #elif defined(CONFIG_GNUTLS) gnutls_anon_client_credentials_t anon_cred = NULL; gnutls_certificate_credentials_t xcred = NULL; const static int kx_priority[16] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, /* Do not use anonymous authentication, unless you know what that means */ GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA_EXPORT, 0 }; const static int cipher_priority[16] = { GNUTLS_CIPHER_RIJNDAEL_128_CBC, GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_ARCFOUR_40, 0 }; const static int cert_type_priority[16] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 }; static void init_gnutls(struct module *module) { int ret = gnutls_global_init(); if (ret < 0) INTERNAL("GNUTLS init failed: %s", gnutls_strerror(ret)); ret = gnutls_anon_allocate_client_credentials(&anon_cred); if (ret < 0) INTERNAL("GNUTLS anon credentials alloc failed: %s", gnutls_strerror(ret)); ret = gnutls_certificate_allocate_credentials(&xcred); if (ret < 0) INTERNAL("GNUTLS X509 credentials alloc failed: %s", gnutls_strerror(ret)); /* Here, we should load certificate files etc. */ } static void done_gnutls(struct module *module) { if (xcred) gnutls_certificate_free_credentials(xcred); if (anon_cred) gnutls_anon_free_client_credentials(anon_cred); gnutls_global_deinit(); } static struct option_info gnutls_options[] = { INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"), "cert_verify", 0, 0, N_("Verify the peer's SSL certificate. Note that this\n" "probably doesn't work properly at all with GnuTLS.")), NULL_OPTION_INFO, }; static struct module gnutls_module = struct_module( /* name: */ "GnuTLS", /* options: */ gnutls_options, /* events: */ NULL, /* submodules: */ NULL, /* data: */ NULL, /* init: */ init_gnutls, /* done: */ done_gnutls ); #endif /* CONFIG_OPENSSL or CONFIG_GNUTLS */ static struct option_info ssl_options[] = { INIT_OPT_TREE("connection", N_("SSL"), "ssl", OPT_SORT, N_("SSL options.")), NULL_OPTION_INFO, }; static struct module *ssl_modules[] = { #ifdef CONFIG_OPENSSL &openssl_module, #elif defined(CONFIG_GNUTLS) &gnutls_module, #endif NULL, }; struct module ssl_module = struct_module( /* name: */ N_("SSL"), /* options: */ ssl_options, /* events: */ NULL, /* submodules: */ ssl_modules, /* data: */ NULL, /* init: */ NULL, /* done: */ NULL ); int init_ssl_connection(struct socket *socket) { #ifdef CONFIG_OPENSSL socket->ssl = SSL_new(context); if (!socket->ssl) return S_SSL_ERROR; #elif defined(CONFIG_GNUTLS) const unsigned char server_name[] = "localhost"; ssl_t *state = mem_alloc(sizeof(ssl_t)); if (!state) return S_SSL_ERROR; if (gnutls_init(state, GNUTLS_CLIENT) < 0) { /* DBG("sslinit %s", gnutls_strerror(ret)); */ mem_free(state); return S_SSL_ERROR; } if (gnutls_cred_set(*state, GNUTLS_CRD_ANON, anon_cred) < 0) { /* DBG("sslanoncred %s", gnutls_strerror(ret)); */ gnutls_deinit(*state); mem_free(state); return S_SSL_ERROR; } if (gnutls_cred_set(*state, GNUTLS_CRD_CERTIFICATE, xcred) < 0) { /* DBG("sslx509cred %s", gnutls_strerror(ret)); */ gnutls_deinit(*state); mem_free(state); return S_SSL_ERROR; } gnutls_set_default_priority(*state); gnutls_handshake_set_private_extensions(*state, 1); gnutls_cipher_set_priority(*state, cipher_priority); gnutls_kx_set_priority(*state, kx_priority); gnutls_certificate_type_set_priority(*state, cert_type_priority); gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name, sizeof(server_name) - 1); socket->ssl = state; #endif return S_OK; } void done_ssl_connection(struct socket *socket) { ssl_t *ssl = socket->ssl; if (!ssl) return; #ifdef CONFIG_OPENSSL SSL_free(ssl); #elif defined(CONFIG_GNUTLS) gnutls_deinit(*ssl); mem_free(ssl); #endif socket->ssl = NULL; } unsigned char * get_ssl_connection_cipher(struct socket *socket) { ssl_t *ssl = socket->ssl; struct string str; if (!init_string(&str)) return NULL; #ifdef CONFIG_OPENSSL add_format_to_string(&str, "%ld-bit %s %s", SSL_get_cipher_bits(ssl, NULL), SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl)); #elif defined(CONFIG_GNUTLS) /* XXX: How to get other relevant parameters? */ add_format_to_string(&str, "%s - %s - %s - %s - %s (compr: %s)", gnutls_protocol_get_name(gnutls_protocol_get_version(*ssl)), gnutls_kx_get_name(gnutls_kx_get(*ssl)), gnutls_cipher_get_name(gnutls_cipher_get(*ssl)), gnutls_mac_get_name(gnutls_mac_get(*ssl)), gnutls_certificate_type_get_name(gnutls_certificate_type_get(*ssl)), gnutls_compression_get_name(gnutls_compression_get(*ssl))); #endif return str.source; }