bb1ae07378788955a7ed61a5ffa09780cddb05ae
[elinks.git] / src / network / ssl / ssl.c
blobbb1ae07378788955a7ed61a5ffa09780cddb05ae
1 /* SSL support - wrappers for SSL routines */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #ifdef CONFIG_OPENSSL
8 #include <openssl/ssl.h>
9 #include <openssl/rand.h>
10 #define USE_OPENSSL
11 #elif defined(CONFIG_NSS_COMPAT_OSSL)
12 #include <nss_compat_ossl/nss_compat_ossl.h>
13 #define USE_OPENSSL
14 #elif defined(CONFIG_GNUTLS)
15 #include <gcrypt.h>
16 #include <gnutls/gnutls.h>
17 #include <gnutls/x509.h>
18 #else
19 #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
20 #endif
22 #ifdef HAVE_LIMITS_H
23 #include <limits.h>
24 #endif
26 #include "elinks.h"
28 #include "intl/gettext/libintl.h"
29 #include "main/module.h"
30 #include "network/connection.h"
31 #include "network/socket.h"
32 #include "network/ssl/ssl.h"
33 #include "util/conv.h"
34 #include "util/error.h"
35 #include "util/string.h"
36 #include "util/random.h"
39 /* FIXME: As you can see, SSL is currently implemented in very, erm,
40 * decentralized manner. */
42 #ifdef USE_OPENSSL
44 #ifndef PATH_MAX
45 #define PATH_MAX 256 /* according to my /usr/include/bits/posix1_lim.h */
46 #endif
48 SSL_CTX *context = NULL;
50 static void
51 init_openssl(struct module *module)
53 unsigned char f_randfile[PATH_MAX];
55 /* In a nutshell, on OS's without a /dev/urandom, the OpenSSL library
56 * cannot initialize the PRNG and so every attempt to use SSL fails.
57 * It's actually an OpenSSL FAQ, and according to them, it's up to the
58 * application coders to seed the RNG. -- William Yodlowsky */
59 if (RAND_egd(RAND_file_name(f_randfile, sizeof(f_randfile))) < 0) {
60 /* Not an EGD, so read and write to it */
61 if (RAND_load_file(f_randfile, -1))
62 RAND_write_file(f_randfile);
65 SSLeay_add_ssl_algorithms();
66 context = SSL_CTX_new(SSLv23_client_method());
67 SSL_CTX_set_options(context, SSL_OP_ALL);
68 SSL_CTX_set_default_verify_paths(context);
71 static void
72 done_openssl(struct module *module)
74 if (context) SSL_CTX_free(context);
77 static struct option_info openssl_options[] = {
78 INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
79 "cert_verify", 0, 0,
80 N_("Verify the peer's SSL certificate. Note that this "
81 "needs extensive configuration of OpenSSL by the user.")),
83 INIT_OPT_TREE("connection.ssl", N_("Client Certificates"),
84 "client_cert", OPT_SORT,
85 N_("X509 client certificate options.")),
87 INIT_OPT_BOOL("connection.ssl.client_cert", N_("Enable"),
88 "enable", 0, 0,
89 N_("Enable or not the sending of X509 client certificates "
90 "to servers which request them.")),
92 #ifdef CONFIG_NSS_COMPAT_OSSL
93 INIT_OPT_STRING("connection.ssl.client_cert", N_("Certificate nickname"),
94 "nickname", 0, "",
95 N_("The nickname of the client certificate stored in NSS "
96 "database. If this value is unset, the nickname from "
97 "the X509_CLIENT_CERT variable is used instead. If you "
98 "have a PKCS#12 file containing client certificate, you "
99 "can import it into your NSS database with:\n"
100 "\n"
101 "$ pk12util -i mycert.p12 -d /path/to/database\n"
102 "\n"
103 "The NSS database location can be changed by SSL_DIR "
104 "environment variable. The database can be also shared "
105 "with Mozilla browsers.")),
106 #else
107 INIT_OPT_STRING("connection.ssl.client_cert", N_("Certificate File"),
108 "file", 0, "",
109 N_("The location of a file containing the client certificate "
110 "and unencrypted private key in PEM format. If unset, the "
111 "file pointed to by the X509_CLIENT_CERT variable is used "
112 "instead.")),
113 #endif
115 NULL_OPTION_INFO,
118 static struct module openssl_module = struct_module(
119 /* name: */ "OpenSSL",
120 /* options: */ openssl_options,
121 /* events: */ NULL,
122 /* submodules: */ NULL,
123 /* data: */ NULL,
124 /* init: */ init_openssl,
125 /* done: */ done_openssl
128 #elif defined(CONFIG_GNUTLS)
130 static gnutls_anon_client_credentials_t anon_cred = NULL;
131 static gnutls_certificate_credentials_t xcred = NULL;
133 #if 0
134 const static int kx_priority[16] = {
135 GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP,
136 /* Do not use anonymous authentication, unless you know what that means */
137 GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA_EXPORT, 0
139 const static int cipher_priority[16] = {
140 GNUTLS_CIPHER_RIJNDAEL_128_CBC, GNUTLS_CIPHER_ARCFOUR_128,
141 GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_ARCFOUR_40, 0
143 const static int cert_type_priority[16] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
144 #endif
146 static void
147 init_gnutls(struct module *module)
149 int ret = gnutls_global_init();
150 unsigned char *ca_file = get_opt_str("connection.ssl.trusted_ca_file",
151 NULL);
153 if (ret < 0)
154 INTERNAL("GNUTLS init failed: %s", gnutls_strerror(ret));
156 ret = gnutls_anon_allocate_client_credentials(&anon_cred);
157 if (ret < 0)
158 INTERNAL("GNUTLS anon credentials alloc failed: %s",
159 gnutls_strerror(ret));
161 ret = gnutls_certificate_allocate_credentials(&xcred);
162 if (ret < 0)
163 INTERNAL("GNUTLS X509 credentials alloc failed: %s",
164 gnutls_strerror(ret));
165 /* Here, we should load certificate files etc. */
166 if (*ca_file) {
167 /* FIXME: check returned values. --witekfl */
168 gnutls_certificate_set_x509_trust_file(xcred, ca_file,
169 GNUTLS_X509_FMT_PEM);
171 gnutls_certificate_set_verify_flags(xcred,
172 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
177 static void
178 done_gnutls(struct module *module)
180 if (xcred) gnutls_certificate_free_credentials(xcred);
181 if (anon_cred) gnutls_anon_free_client_credentials(anon_cred);
182 gnutls_global_deinit();
185 static struct option_info gnutls_options[] = {
186 INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
187 "cert_verify", 0, 0,
188 N_("Verify the peer's SSL certificate. If you enable "
189 "this, set also \"Trusted CA file\".")),
191 /* The default value of the following option points to a file
192 * generated by the ca-certificates Debian package. Don't use
193 * CONFDIR here: if someone installs ELinks in $HOME and wants
194 * to have a user-specific trust list, he or she can just
195 * change the file name via the option manager. Distributors
196 * of binary packages should of course change the default to
197 * suit their systems.
198 * TODO: If the file name is relative, look in elinks_home? */
199 INIT_OPT_STRING("connection.ssl", N_("Trusted CA file"),
200 "trusted_ca_file", 0, "/etc/ssl/certs/ca-certificates.crt",
201 N_("The location of a file containing certificates of "
202 "trusted certification authorities in PEM format. "
203 "ELinks then trusts certificates issued by these CAs.\n"
204 "\n"
205 "If you change this option or the file, you must "
206 "restart ELinks for the changes to take effect. "
207 "This option affects GnuTLS but not OpenSSL.")),
209 NULL_OPTION_INFO,
212 static struct module gnutls_module = struct_module(
213 /* name: */ "GnuTLS",
214 /* options: */ gnutls_options,
215 /* events: */ NULL,
216 /* submodules: */ NULL,
217 /* data: */ NULL,
218 /* init: */ init_gnutls,
219 /* done: */ done_gnutls
222 #endif /* USE_OPENSSL or CONFIG_GNUTLS */
224 static struct option_info ssl_options[] = {
225 INIT_OPT_TREE("connection", N_("SSL"),
226 "ssl", OPT_SORT,
227 N_("SSL options.")),
229 NULL_OPTION_INFO,
232 static struct module *ssl_modules[] = {
233 #ifdef USE_OPENSSL
234 &openssl_module,
235 #elif defined(CONFIG_GNUTLS)
236 &gnutls_module,
237 #endif
238 NULL,
241 struct module ssl_module = struct_module(
242 /* name: */ N_("SSL"),
243 /* options: */ ssl_options,
244 /* events: */ NULL,
245 /* submodules: */ ssl_modules,
246 /* data: */ NULL,
247 /* init: */ NULL,
248 /* done: */ NULL
252 init_ssl_connection(struct socket *socket,
253 const unsigned char *server_name)
255 #ifdef USE_OPENSSL
256 socket->ssl = SSL_new(context);
257 if (!socket->ssl) return S_SSL_ERROR;
259 /* If the server name is known, pass it to OpenSSL.
261 * The return value of SSL_set_tlsext_host_name is not
262 * documented. The source shows that it returns 1 if
263 * successful; on error, it calls SSLerr and returns 0. */
264 if (server_name
265 && !SSL_set_tlsext_host_name(socket->ssl, server_name)) {
266 SSL_free(socket->ssl);
267 socket->ssl = NULL;
268 return S_SSL_ERROR;
271 #elif defined(CONFIG_GNUTLS)
272 ssl_t *state = mem_alloc(sizeof(ssl_t));
274 if (!state) return S_SSL_ERROR;
276 if (gnutls_init(state, GNUTLS_CLIENT) < 0) {
277 /* DBG("sslinit %s", gnutls_strerror(ret)); */
278 mem_free(state);
279 return S_SSL_ERROR;
282 if (gnutls_cred_set(*state, GNUTLS_CRD_ANON, anon_cred) < 0) {
283 /* DBG("sslanoncred %s", gnutls_strerror(ret)); */
284 gnutls_deinit(*state);
285 mem_free(state);
286 return S_SSL_ERROR;
289 if (gnutls_cred_set(*state, GNUTLS_CRD_CERTIFICATE, xcred) < 0) {
290 /* DBG("sslx509cred %s", gnutls_strerror(ret)); */
291 gnutls_deinit(*state);
292 mem_free(state);
293 return S_SSL_ERROR;
296 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
297 /* Disable OpenPGP certificates because they are not widely
298 * used and ELinks does not yet support verifying them.
299 * Besides, in GnuTLS < 2.4.0, they require the gnutls-extra
300 * library, whose GPLv3+ is not compatible with GPLv2 of
301 * ELinks.
303 * Disable TLS1.1 because https://bugzilla.novell.com/ does
304 * not reply to it and leaves the connection open so that
305 * ELinks does not detect an SSL error but rather times out.
306 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=528661#25
308 * There is another gnutls_priority_set_direct call elsewhere
309 * in ELinks. If you change the priorities here, please check
310 * whether that one needs to be changed as well. */
311 if (gnutls_priority_set_direct(*state,
312 "NORMAL:-CTYPE-OPENPGP:-VERS-TLS1.1",
313 NULL)) {
314 gnutls_deinit(*state);
315 mem_free(state);
316 return S_SSL_ERROR;
318 #else
319 gnutls_set_default_priority(*state);
320 #endif
321 #if 0
322 /* Deprecated functions */
323 /* gnutls_handshake_set_private_extensions(*state, 1); */
324 gnutls_cipher_set_priority(*state, cipher_priority);
325 gnutls_kx_set_priority(*state, kx_priority);
326 /* gnutls_certificate_type_set_priority(*state, cert_type_priority); */
327 #endif
329 if (server_name
330 && gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name,
331 strlen(server_name))) {
332 gnutls_deinit(*state);
333 mem_free(state);
334 return S_SSL_ERROR;
337 socket->ssl = state;
338 #endif
340 return S_OK;
343 void
344 done_ssl_connection(struct socket *socket)
346 ssl_t *ssl = socket->ssl;
348 if (!ssl) return;
349 #ifdef USE_OPENSSL
350 SSL_free(ssl);
351 #elif defined(CONFIG_GNUTLS)
352 gnutls_deinit(*ssl);
353 mem_free(ssl);
354 #endif
355 socket->ssl = NULL;
358 unsigned char *
359 get_ssl_connection_cipher(struct socket *socket)
361 ssl_t *ssl = socket->ssl;
362 struct string str;
364 if (!init_string(&str)) return NULL;
366 #ifdef USE_OPENSSL
367 add_format_to_string(&str, "%ld-bit %s %s",
368 SSL_get_cipher_bits(ssl, NULL),
369 SSL_get_cipher_version(ssl),
370 SSL_get_cipher_name(ssl));
371 #elif defined(CONFIG_GNUTLS)
372 /* XXX: How to get other relevant parameters? */
373 add_format_to_string(&str, "%s - %s - %s - %s - %s (compr: %s)",
374 gnutls_protocol_get_name(gnutls_protocol_get_version(*ssl)),
375 gnutls_kx_get_name(gnutls_kx_get(*ssl)),
376 gnutls_cipher_get_name(gnutls_cipher_get(*ssl)),
377 gnutls_mac_get_name(gnutls_mac_get(*ssl)),
378 gnutls_certificate_type_get_name(gnutls_certificate_type_get(*ssl)),
379 gnutls_compression_get_name(gnutls_compression_get(*ssl)));
380 #endif
382 return str.source;
385 /* When CONFIG_SSL is defined, this implementation replaces the one in
386 * src/util/random.c. */
387 void
388 random_nonce(unsigned char buf[], size_t size)
390 #ifdef USE_OPENSSL
391 RAND_pseudo_bytes(buf, size);
392 #elif defined(CONFIG_GNUTLS)
393 gcry_create_nonce(buf, size);
394 #else
395 # error unsupported SSL library
396 #endif