bug 764: Initialize the right member of union option_value
[elinks.git] / src / network / ssl / ssl.c
blobc008121dc6956d62d56bd96650414f4c9ab79262
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 #elif defined(CONFIG_GNUTLS)
11 #include <gnutls/gnutls.h>
12 #include <gnutls/x509.h>
13 #else
14 #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
15 #endif
17 #ifdef HAVE_LIMITS_H
18 #include <limits.h>
19 #endif
21 #include "elinks.h"
23 #include "intl/gettext/libintl.h"
24 #include "main/module.h"
25 #include "network/connection.h"
26 #include "network/socket.h"
27 #include "network/ssl/ssl.h"
28 #include "util/conv.h"
29 #include "util/error.h"
30 #include "util/string.h"
33 /* FIXME: As you can see, SSL is currently implemented in very, erm,
34 * decentralized manner. */
36 #ifdef CONFIG_OPENSSL
38 #ifndef PATH_MAX
39 #define PATH_MAX 256 /* according to my /usr/include/bits/posix1_lim.h */
40 #endif
42 SSL_CTX *context = NULL;
44 static void
45 init_openssl(struct module *module)
47 unsigned char f_randfile[PATH_MAX];
49 /* In a nutshell, on OS's without a /dev/urandom, the OpenSSL library
50 * cannot initialize the PRNG and so every attempt to use SSL fails.
51 * It's actually an OpenSSL FAQ, and according to them, it's up to the
52 * application coders to seed the RNG. -- William Yodlowsky */
53 if (RAND_egd(RAND_file_name(f_randfile, sizeof(f_randfile))) < 0) {
54 /* Not an EGD, so read and write to it */
55 if (RAND_load_file(f_randfile, -1))
56 RAND_write_file(f_randfile);
59 SSLeay_add_ssl_algorithms();
60 context = SSL_CTX_new(SSLv23_client_method());
61 SSL_CTX_set_options(context, SSL_OP_ALL);
62 SSL_CTX_set_default_verify_paths(context);
65 static void
66 done_openssl(struct module *module)
68 if (context) SSL_CTX_free(context);
71 static union option_info openssl_options[] = {
72 INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
73 "cert_verify", 0, 0,
74 N_("Verify the peer's SSL certificate. Note that this "
75 "needs extensive configuration of OpenSSL by the user.")),
77 INIT_OPT_TREE("connection.ssl", N_("Client Certificates"),
78 "client_cert", OPT_SORT,
79 N_("X509 client certificate options.")),
81 INIT_OPT_BOOL("connection.ssl.client_cert", N_("Enable"),
82 "enable", 0, 0,
83 N_("Enable or not the sending of X509 client certificates "
84 "to servers which request them.")),
86 INIT_OPT_STRING("connection.ssl.client_cert", N_("Certificate File"),
87 "file", 0, "",
88 N_("The location of a file containing the client certificate "
89 "and unencrypted private key in PEM format. If unset, the "
90 "file pointed to by the X509_CLIENT_CERT variable is used "
91 "instead.")),
93 NULL_OPTION_INFO,
96 static struct module openssl_module = struct_module(
97 /* name: */ "OpenSSL",
98 /* options: */ openssl_options,
99 /* events: */ NULL,
100 /* submodules: */ NULL,
101 /* data: */ NULL,
102 /* init: */ init_openssl,
103 /* done: */ done_openssl
106 #elif defined(CONFIG_GNUTLS)
108 gnutls_anon_client_credentials_t anon_cred = NULL;
109 gnutls_certificate_credentials_t xcred = NULL;
111 const static int kx_priority[16] = {
112 GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP,
113 /* Do not use anonymous authentication, unless you know what that means */
114 GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA_EXPORT, 0
116 const static int cipher_priority[16] = {
117 GNUTLS_CIPHER_RIJNDAEL_128_CBC, GNUTLS_CIPHER_ARCFOUR_128,
118 GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_ARCFOUR_40, 0
120 const static int cert_type_priority[16] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
122 static void
123 init_gnutls(struct module *module)
125 int ret = gnutls_global_init();
126 unsigned char *ca_file = get_opt_str("connection.ssl.trusted_ca_file");
128 if (ret < 0)
129 INTERNAL("GNUTLS init failed: %s", gnutls_strerror(ret));
131 ret = gnutls_anon_allocate_client_credentials(&anon_cred);
132 if (ret < 0)
133 INTERNAL("GNUTLS anon credentials alloc failed: %s",
134 gnutls_strerror(ret));
136 ret = gnutls_certificate_allocate_credentials(&xcred);
137 if (ret < 0)
138 INTERNAL("GNUTLS X509 credentials alloc failed: %s",
139 gnutls_strerror(ret));
140 /* Here, we should load certificate files etc. */
141 if (*ca_file) {
142 /* FIXME: check returned values. --witekfl */
143 gnutls_certificate_set_x509_trust_file(xcred, ca_file,
144 GNUTLS_X509_FMT_PEM);
146 gnutls_certificate_set_verify_flags(xcred,
147 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
152 static void
153 done_gnutls(struct module *module)
155 if (xcred) gnutls_certificate_free_credentials(xcred);
156 if (anon_cred) gnutls_anon_free_client_credentials(anon_cred);
157 gnutls_global_deinit();
160 static union option_info gnutls_options[] = {
161 INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
162 "cert_verify", 0, 0,
163 N_("Verify the peer's SSL certificate. If you enable "
164 "this, set also \"Trusted CA file\".")),
166 /* The default value of the following option points to a file
167 * generated by the ca-certificates Debian package. Don't use
168 * CONFDIR here: if someone installs ELinks in $HOME and wants
169 * to have a user-specific trust list, he or she can just
170 * change the file name via the option manager. Distributors
171 * of binary packages should of course change the default to
172 * suit their systems.
173 * TODO: If the file name is relative, look in elinks_home? */
174 INIT_OPT_STRING("connection.ssl", N_("Trusted CA file"),
175 "trusted_ca_file", 0, "/etc/ssl/certs/ca-certificates.crt",
176 N_("The location of a file containing certificates of "
177 "trusted certification authorities in PEM format. "
178 "ELinks then trusts certificates issued by these CAs.\n"
179 "\n"
180 "If you change this option or the file, you must "
181 "restart ELinks for the changes to take effect. "
182 "This option affects GnuTLS but not OpenSSL.")),
184 NULL_OPTION_INFO,
187 static struct module gnutls_module = struct_module(
188 /* name: */ "GnuTLS",
189 /* options: */ gnutls_options,
190 /* events: */ NULL,
191 /* submodules: */ NULL,
192 /* data: */ NULL,
193 /* init: */ init_gnutls,
194 /* done: */ done_gnutls
197 #endif /* CONFIG_OPENSSL or CONFIG_GNUTLS */
199 static union option_info ssl_options[] = {
200 INIT_OPT_TREE("connection", N_("SSL"),
201 "ssl", OPT_SORT,
202 N_("SSL options.")),
204 NULL_OPTION_INFO,
207 static struct module *ssl_modules[] = {
208 #ifdef CONFIG_OPENSSL
209 &openssl_module,
210 #elif defined(CONFIG_GNUTLS)
211 &gnutls_module,
212 #endif
213 NULL,
216 struct module ssl_module = struct_module(
217 /* name: */ N_("SSL"),
218 /* options: */ ssl_options,
219 /* events: */ NULL,
220 /* submodules: */ ssl_modules,
221 /* data: */ NULL,
222 /* init: */ NULL,
223 /* done: */ NULL
227 init_ssl_connection(struct socket *socket)
229 #ifdef CONFIG_OPENSSL
230 socket->ssl = SSL_new(context);
231 if (!socket->ssl) return S_SSL_ERROR;
232 #elif defined(CONFIG_GNUTLS)
233 /* const unsigned char server_name[] = "localhost"; */
234 ssl_t *state = mem_alloc(sizeof(ssl_t));
236 if (!state) return S_SSL_ERROR;
238 if (gnutls_init(state, GNUTLS_CLIENT) < 0) {
239 /* DBG("sslinit %s", gnutls_strerror(ret)); */
240 mem_free(state);
241 return S_SSL_ERROR;
244 if (gnutls_cred_set(*state, GNUTLS_CRD_ANON, anon_cred) < 0) {
245 /* DBG("sslanoncred %s", gnutls_strerror(ret)); */
246 gnutls_deinit(*state);
247 mem_free(state);
248 return S_SSL_ERROR;
251 if (gnutls_cred_set(*state, GNUTLS_CRD_CERTIFICATE, xcred) < 0) {
252 /* DBG("sslx509cred %s", gnutls_strerror(ret)); */
253 gnutls_deinit(*state);
254 mem_free(state);
255 return S_SSL_ERROR;
258 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
259 if (gnutls_priority_set_direct(*state, "NORMAL:-CTYPE-OPENPGP", NULL)) {
260 gnutls_deinit(*state);
261 mem_free(state);
262 return S_SSL_ERROR;
264 #else
265 gnutls_set_default_priority(*state);
266 #endif
267 /* gnutls_handshake_set_private_extensions(*state, 1); */
268 gnutls_cipher_set_priority(*state, cipher_priority);
269 gnutls_kx_set_priority(*state, kx_priority);
270 /* gnutls_certificate_type_set_priority(*state, cert_type_priority);
271 gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name,
272 sizeof(server_name) - 1); */
274 socket->ssl = state;
275 #endif
277 return S_OK;
280 void
281 done_ssl_connection(struct socket *socket)
283 ssl_t *ssl = socket->ssl;
285 if (!ssl) return;
286 #ifdef CONFIG_OPENSSL
287 SSL_free(ssl);
288 #elif defined(CONFIG_GNUTLS)
289 gnutls_deinit(*ssl);
290 mem_free(ssl);
291 #endif
292 socket->ssl = NULL;
295 unsigned char *
296 get_ssl_connection_cipher(struct socket *socket)
298 ssl_t *ssl = socket->ssl;
299 struct string str;
301 if (!init_string(&str)) return NULL;
303 #ifdef CONFIG_OPENSSL
304 add_format_to_string(&str, "%ld-bit %s %s",
305 SSL_get_cipher_bits(ssl, NULL),
306 SSL_get_cipher_version(ssl),
307 SSL_get_cipher_name(ssl));
308 #elif defined(CONFIG_GNUTLS)
309 /* XXX: How to get other relevant parameters? */
310 add_format_to_string(&str, "%s - %s - %s - %s - %s (compr: %s)",
311 gnutls_protocol_get_name(gnutls_protocol_get_version(*ssl)),
312 gnutls_kx_get_name(gnutls_kx_get(*ssl)),
313 gnutls_cipher_get_name(gnutls_cipher_get(*ssl)),
314 gnutls_mac_get_name(gnutls_mac_get(*ssl)),
315 gnutls_certificate_type_get_name(gnutls_certificate_type_get(*ssl)),
316 gnutls_compression_get_name(gnutls_compression_get(*ssl)));
317 #endif
319 return str.source;