b449173e3a2c8206d0a78a9fd4ca9b8b17687b3b
[elinks.git] / src / network / ssl / socket.c
blobb449173e3a2c8206d0a78a9fd4ca9b8b17687b3b
1 /* SSL socket workshop */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #ifdef CONFIG_OPENSSL
8 #include <openssl/ssl.h>
9 #define USE_OPENSSL
10 #elif defined(CONFIG_NSS_COMPAT_OSSL)
11 #include <nss_compat_ossl/nss_compat_ossl.h>
12 #define USE_OPENSSL
13 #elif defined(CONFIG_GNUTLS)
14 #include <gnutls/gnutls.h>
15 #include <gnutls/x509.h>
16 #else
17 #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
18 #endif
20 #include <errno.h>
22 #include "elinks.h"
24 #include "config/options.h"
25 #include "main/select.h"
26 #include "network/connection.h"
27 #include "network/socket.h"
28 #include "network/ssl/socket.h"
29 #include "network/ssl/ssl.h"
30 #include "protocol/uri.h"
31 #include "util/memory.h"
34 /* SSL errors */
35 #ifdef USE_OPENSSL
36 #define SSL_ERROR_WANT_READ2 9999 /* XXX */
37 #define SSL_ERROR_WANT_WRITE2 SSL_ERROR_WANT_WRITE
38 #define SSL_ERROR_SYSCALL2 SSL_ERROR_SYSCALL
39 #elif defined(CONFIG_GNUTLS)
40 #define SSL_ERROR_NONE GNUTLS_E_SUCCESS
41 #define SSL_ERROR_WANT_READ GNUTLS_E_AGAIN
42 #define SSL_ERROR_WANT_READ2 GNUTLS_E_INTERRUPTED
43 #define SSL_ERROR_WANT_WRITE GNUTLS_E_AGAIN
44 #define SSL_ERROR_WANT_WRITE2 GNUTLS_E_INTERRUPTED
45 #define SSL_ERROR_SYSCALL GNUTLS_E_PUSH_ERROR
46 #define SSL_ERROR_SYSCALL2 GNUTLS_E_PULL_ERROR
47 #endif
49 #ifdef USE_OPENSSL
51 #define ssl_do_connect(socket) SSL_get_error(socket->ssl, SSL_connect(socket->ssl))
52 #define ssl_do_write(socket, data, len) SSL_write(socket->ssl, data, len)
53 #define ssl_do_read(socket, data, len) SSL_read(socket->ssl, data, len)
54 #define ssl_do_close(socket) /* Hmh? No idea.. */
56 #elif defined(CONFIG_GNUTLS)
58 #define ssl_do_connect(conn) gnutls_handshake(*((ssl_t *) socket->ssl))
59 #define ssl_do_write(socket, data, len) gnutls_record_send(*((ssl_t *) socket->ssl), data, len)
60 #define ssl_do_read(socket, data, len) gnutls_record_recv(*((ssl_t *) socket->ssl), data, len)
61 /* We probably don't handle this entirely correctly.. */
62 #define ssl_do_close(socket) gnutls_bye(*((ssl_t *) socket->ssl), GNUTLS_SHUT_RDWR);
64 #endif
67 /* Refuse to negotiate TLS 1.0 and later protocols on @socket->ssl.
68 * Without this, connecting to <https://www-s.uiuc.edu/> with GnuTLS
69 * 1.3.5 would result in an SSL error. The bug may be in the server
70 * (Netscape-Enterprise/3.6 SP3), in GnuTLS, or in ELinks; please log
71 * your findings to ELinks bug 712. */
72 static void
73 ssl_set_no_tls(struct socket *socket)
75 #ifdef CONFIG_OPENSSL
76 ((ssl_t *) socket->ssl)->options |= SSL_OP_NO_TLSv1;
77 #elif defined(CONFIG_GNUTLS)
78 /* There is another gnutls_priority_set_direct call elsewhere
79 * in ELinks. If you change the priorities here, please check
80 * whether that one needs to be changed as well.
82 * GnuTLS 2.12.x is said to support "-VERS-TLS-ALL" too, but
83 * that version hasn't yet been released as of May 2011. */
84 gnutls_priority_set_direct(*(ssl_t *) socket->ssl,
85 "SECURE:-CTYPE-OPENPGP"
86 ":+VERS-SSL3.0:-VERS-TLS1.0"
87 ":-VERS-TLS1.1:-VERS-TLS1.2"
88 ":%SSL3_RECORD_VERSION",
89 NULL);
90 #endif
94 #ifdef CONFIG_GNUTLS
95 static int
96 verify_certificates(struct socket *socket)
98 gnutls_x509_crt_t cert;
99 gnutls_session_t session = *(ssl_t *)socket->ssl;
100 struct connection *conn = socket->conn;
101 const gnutls_datum_t *cert_list;
102 unsigned char *hostname;
103 int ret;
104 unsigned int cert_list_size, status;
107 ret = gnutls_certificate_verify_peers2(session, &status);
108 if (ret) return ret;
109 if (status) return status;
111 /* If the certificate is of a type for which verification has
112 * not yet been implemented, then reject it. This way, a fake
113 * server cannot avoid verification by using a strange type of
114 * certificate.
116 * OpenPGP certificates shouldn't even get this far anyway,
117 * because init_ssl_connection() tells GnuTLS to disable
118 * OpenPGP, and ELinks never calls
119 * gnutls_certificate_set_openpgp_keyring_file, so status
120 * should have been GNUTLS_CERT_SIGNER_NOT_FOUND. */
121 if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
122 return -7;
124 if (gnutls_x509_crt_init(&cert) < 0) {
125 return -1;
128 cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
129 if (!cert_list) {
130 return -2;
133 if (gnutls_x509_crt_import(cert, &cert_list[0],
134 GNUTLS_X509_FMT_DER) < 0) {
135 return -3;
137 if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) {
138 gnutls_x509_crt_deinit(cert);
139 return -4;
142 if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) {
143 gnutls_x509_crt_deinit(cert);
144 return -5;
146 hostname = get_uri_string(conn->uri, URI_HOST);
147 if (!hostname) return -6;
149 ret = !gnutls_x509_crt_check_hostname(cert, hostname);
150 gnutls_x509_crt_deinit(cert);
151 mem_free(hostname);
152 return ret;
154 #endif /* CONFIG_GNUTLS */
156 static void
157 ssl_want_read(struct socket *socket)
159 if (socket->no_tls)
160 ssl_set_no_tls(socket);
162 switch (ssl_do_connect(socket)) {
163 case SSL_ERROR_NONE:
164 #ifdef CONFIG_GNUTLS
165 if (get_opt_bool("connection.ssl.cert_verify", NULL)
166 && verify_certificates(socket)) {
167 socket->ops->retry(socket, connection_state(S_SSL_ERROR));
168 return;
170 #endif
172 /* Report successful SSL connection setup. */
173 complete_connect_socket(socket, NULL, NULL);
174 break;
176 case SSL_ERROR_WANT_READ:
177 case SSL_ERROR_WANT_READ2:
178 break;
180 default:
181 socket->no_tls = !socket->no_tls;
182 socket->ops->retry(socket, connection_state(S_SSL_ERROR));
186 /* Return -1 on error, 0 or success. */
188 ssl_connect(struct socket *socket)
190 int ret;
191 unsigned char *server_name;
192 struct connection *conn = socket->conn;
194 /* TODO: Recode server_name to UTF-8. */
195 server_name = get_uri_string(conn->proxied_uri, URI_HOST);
196 if (!server_name) {
197 socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
198 return -1;
201 /* RFC 3546 says literal IPv4 and IPv6 addresses are not allowed. */
202 if (is_ip_address(server_name, strlen(server_name)))
203 mem_free_set(&server_name, NULL);
205 if (init_ssl_connection(socket, server_name) == S_SSL_ERROR) {
206 mem_free_if(server_name);
207 socket->ops->done(socket, connection_state(S_SSL_ERROR));
208 return -1;
211 mem_free_if(server_name);
213 if (socket->no_tls)
214 ssl_set_no_tls(socket);
216 #ifdef USE_OPENSSL
217 SSL_set_fd(socket->ssl, socket->fd);
219 if (get_opt_bool("connection.ssl.cert_verify", NULL))
220 SSL_set_verify(socket->ssl, SSL_VERIFY_PEER
221 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
222 NULL);
224 if (get_opt_bool("connection.ssl.client_cert.enable", NULL)) {
225 unsigned char *client_cert;
227 #ifdef CONFIG_NSS_COMPAT_OSSL
228 client_cert = get_opt_str(
229 "connection.ssl.client_cert.nickname", NULL);
230 #else
231 client_cert = get_opt_str(
232 "connection.ssl.client_cert.file", NULL);
233 #endif
234 if (!*client_cert) {
235 client_cert = getenv("X509_CLIENT_CERT");
236 if (client_cert && !*client_cert)
237 client_cert = NULL;
240 if (client_cert) {
241 #ifdef CONFIG_NSS_COMPAT_OSSL
242 SSL_CTX_use_certificate_chain_file(
243 (SSL *) socket->ssl,
244 client_cert);
245 #else
246 SSL_CTX *ctx = ((SSL *) socket->ssl)->ctx;
248 SSL_CTX_use_certificate_chain_file(ctx, client_cert);
249 SSL_CTX_use_PrivateKey_file(ctx, client_cert,
250 SSL_FILETYPE_PEM);
251 #endif
255 #elif defined(CONFIG_GNUTLS)
256 /* GnuTLS uses function pointers for network I/O. The default
257 * functions take a file descriptor, but it must be passed in
258 * as a pointer. GnuTLS uses the GNUTLS_INT_TO_POINTER and
259 * GNUTLS_POINTER_TO_INT macros for these conversions, but
260 * those are unfortunately not in any public header. So
261 * ELinks must just cast the pointer the best it can and hope
262 * that the conversions match. */
263 gnutls_transport_set_ptr(*((ssl_t *) socket->ssl),
264 (gnutls_transport_ptr) (longptr_T) socket->fd);
266 /* TODO: Some certificates fuss. --pasky */
267 #endif
269 ret = ssl_do_connect(socket);
271 switch (ret) {
272 case SSL_ERROR_WANT_READ:
273 case SSL_ERROR_WANT_READ2:
274 socket->ops->set_state(socket, connection_state(S_SSL_NEG));
275 set_handlers(socket->fd, (select_handler_T) ssl_want_read,
276 NULL, (select_handler_T) dns_exception, socket);
277 return -1;
279 case SSL_ERROR_NONE:
280 #ifdef CONFIG_GNUTLS
281 if (!get_opt_bool("connection.ssl.cert_verify", NULL))
282 break;
284 if (!verify_certificates(socket))
285 #endif
286 break;
288 default:
289 if (ret != SSL_ERROR_NONE) {
290 /* DBG("sslerr %s", gnutls_strerror(ret)); */
291 socket->no_tls = !socket->no_tls;
294 connect_socket(socket, connection_state(S_SSL_ERROR));
295 return -1;
298 return 0;
301 /* Return enum socket_error on error, bytes written on success. */
302 ssize_t
303 ssl_write(struct socket *socket, unsigned char *data, int len)
305 ssize_t wr = ssl_do_write(socket, data, len);
307 if (wr <= 0) {
308 #ifdef USE_OPENSSL
309 int err = SSL_get_error(socket->ssl, wr);
310 #elif defined(CONFIG_GNUTLS)
311 int err = wr;
312 #endif
313 if (err == SSL_ERROR_WANT_WRITE ||
314 err == SSL_ERROR_WANT_WRITE2) {
315 return -1;
318 if (!wr) return SOCKET_CANT_WRITE;
320 if (err == SSL_ERROR_SYSCALL)
321 return SOCKET_SYSCALL_ERROR;
323 errno = S_SSL_ERROR;
324 return SOCKET_INTERNAL_ERROR;
327 return wr;
330 /* Return enum socket_error on error, bytes read on success. */
331 ssize_t
332 ssl_read(struct socket *socket, unsigned char *data, int len)
334 ssize_t rd = ssl_do_read(socket, data, len);
336 if (rd <= 0) {
337 #ifdef USE_OPENSSL
338 int err = SSL_get_error(socket->ssl, rd);
339 #elif defined(CONFIG_GNUTLS)
340 int err = rd;
341 #endif
343 #ifdef CONFIG_GNUTLS
344 if (err == GNUTLS_E_REHANDSHAKE)
345 return -1;
346 #endif
348 if (err == SSL_ERROR_WANT_READ ||
349 err == SSL_ERROR_WANT_READ2) {
350 return SOCKET_SSL_WANT_READ;
353 if (!rd) return SOCKET_CANT_READ;
355 if (err == SSL_ERROR_SYSCALL2)
356 return SOCKET_SYSCALL_ERROR;
358 errno = S_SSL_ERROR;
359 return SOCKET_INTERNAL_ERROR;
362 return rd;
366 ssl_close(struct socket *socket)
368 ssl_do_close(socket);
369 done_ssl_connection(socket);
371 return 0;