0
0
mirror of https://github.com/rkd77/elinks.git synced 2025-10-21 19:54:04 -04:00
Files
elinks/src/network/ssl/socket.c
Petr Baudis 0f6d4310ad Initial commit of the HEAD branch of the ELinks CVS repository, as of
Thu Sep 15 15:57:07 CEST 2005. The previous history can be added to this
by grafting.
2005-09-15 15:58:31 +02:00

331 lines
7.4 KiB
C

/* SSL socket workshop */
/* $Id: socket.c,v 1.128 2005/09/13 16:44:10 pasky Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef CONFIG_OPENSSL
#include <openssl/ssl.h>
#elif defined(CONFIG_GNUTLS)
#include <gnutls/gnutls.h>
#else
#error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
#endif
#include <errno.h>
#include "elinks.h"
#include "config/options.h"
#include "main/select.h"
#include "network/connection.h"
#include "network/socket.h"
#include "network/ssl/socket.h"
#include "network/ssl/ssl.h"
#include "util/memory.h"
/* SSL errors */
#ifdef CONFIG_OPENSSL
#define SSL_ERROR_WANT_READ2 9999 /* XXX */
#define SSL_ERROR_WANT_WRITE2 SSL_ERROR_WANT_WRITE
#define SSL_ERROR_SYSCALL2 SSL_ERROR_SYSCALL
#elif defined(CONFIG_GNUTLS)
#define SSL_ERROR_NONE GNUTLS_E_SUCCESS
#define SSL_ERROR_WANT_READ GNUTLS_E_AGAIN
#define SSL_ERROR_WANT_READ2 GNUTLS_E_INTERRUPTED
#define SSL_ERROR_WANT_WRITE GNUTLS_E_AGAIN
#define SSL_ERROR_WANT_WRITE2 GNUTLS_E_INTERRUPTED
#define SSL_ERROR_SYSCALL GNUTLS_E_PUSH_ERROR
#define SSL_ERROR_SYSCALL2 GNUTLS_E_PULL_ERROR
#endif
#ifdef CONFIG_OPENSSL
#define ssl_do_connect(socket) SSL_get_error(socket->ssl, SSL_connect(socket->ssl))
#define ssl_do_write(socket, data, len) SSL_write(socket->ssl, data, len)
#define ssl_do_read(socket, data, len) SSL_read(socket->ssl, data, len)
#define ssl_do_close(socket) /* Hmh? No idea.. */
#elif defined(CONFIG_GNUTLS)
#define ssl_do_connect(conn) gnutls_handshake(*((ssl_t *) socket->ssl))
#define ssl_do_write(socket, data, len) gnutls_record_send(*((ssl_t *) socket->ssl), data, len)
#define ssl_do_read(socket, data, len) gnutls_record_recv(*((ssl_t *) socket->ssl), data, len)
/* We probably don't handle this entirely correctly.. */
#define ssl_do_close(socket) gnutls_bye(*((ssl_t *) socket->ssl), GNUTLS_SHUT_RDWR);
#endif
static void
ssl_set_no_tls(struct socket *socket)
{
#ifdef CONFIG_OPENSSL
((ssl_t *) socket->ssl)->options |= SSL_OP_NO_TLSv1;
#elif defined(CONFIG_GNUTLS)
/* We do a little more work here, setting up all these priorities (like
* they couldn't have some reasonable defaults there).. */
{
int protocol_priority[3] = {
GNUTLS_TLS1,
GNUTLS_SSL3,
0
};
gnutls_protocol_set_priority(*((ssl_t *) socket->ssl), protocol_priority);
}
/* Note that I have no clue about these; I just put all I found here
* ;-). It is all a bit confusing for me, and I just want this to work.
* Feel free to send me patch removing useless superfluous bloat,
* thanks in advance. --pasky */
{
int cipher_priority[5] = {
GNUTLS_CIPHER_RIJNDAEL_128_CBC,
GNUTLS_CIPHER_3DES_CBC,
GNUTLS_CIPHER_ARCFOUR,
GNUTLS_CIPHER_RIJNDAEL_256_CBC,
0
};
gnutls_cipher_set_priority(*((ssl_t *) socket->ssl), cipher_priority);
}
{
/* Does any httpd support this..? ;) */
int comp_priority[3] = {
GNUTLS_COMP_ZLIB,
GNUTLS_COMP_NULL,
0
};
gnutls_compression_set_priority(*((ssl_t *) socket->ssl), comp_priority);
}
{
int kx_priority[5] = {
GNUTLS_KX_RSA,
GNUTLS_KX_DHE_DSS,
GNUTLS_KX_DHE_RSA,
/* Looks like we don't want SRP, do we? */
GNUTLS_KX_ANON_DH,
0
};
gnutls_kx_set_priority(*((ssl_t *) socket->ssl), kx_priority);
}
{
int mac_priority[3] = {
GNUTLS_MAC_SHA,
GNUTLS_MAC_MD5,
0
};
gnutls_mac_set_priority(*((ssl_t *) socket->ssl), mac_priority);
}
{
int cert_type_priority[2] = {
GNUTLS_CRT_X509,
/* We don't link with -extra now; by time of writing
* this, it's unclear where OpenPGP will end up. */
0
};
gnutls_certificate_type_set_priority(*((ssl_t *) socket->ssl), cert_type_priority);
}
gnutls_dh_set_prime_bits(*((ssl_t *) socket->ssl), 1024);
#endif
}
static void
ssl_want_read(struct socket *socket)
{
if (socket->no_tls)
ssl_set_no_tls(socket);
switch (ssl_do_connect(socket)) {
case SSL_ERROR_NONE:
#ifdef CONFIG_GNUTLS
if (get_opt_bool("connection.ssl.cert_verify")
&& gnutls_certificate_verify_peers(*((ssl_t *) socket->ssl))) {
socket->ops->retry(socket, S_SSL_ERROR);
return;
}
#endif
/* Report successful SSL connection setup. */
complete_connect_socket(socket, NULL, NULL);
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_READ2:
break;
default:
socket->no_tls = 1;
socket->ops->retry(socket, S_SSL_ERROR);
}
}
/* Return -1 on error, 0 or success. */
int
ssl_connect(struct socket *socket)
{
int ret;
if (init_ssl_connection(socket) == S_SSL_ERROR) {
socket->ops->done(socket, S_SSL_ERROR);
return -1;
}
if (socket->no_tls)
ssl_set_no_tls(socket);
#ifdef CONFIG_OPENSSL
SSL_set_fd(socket->ssl, socket->fd);
if (get_opt_bool("connection.ssl.cert_verify"))
SSL_set_verify(socket->ssl, SSL_VERIFY_PEER
| SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL);
if (get_opt_bool("connection.ssl.client_cert.enable")) {
unsigned char *client_cert;
client_cert = get_opt_str("connection.ssl.client_cert.file");
if (!*client_cert) {
client_cert = getenv("X509_CLIENT_CERT");
if (client_cert && !*client_cert)
client_cert = NULL;
}
if (client_cert) {
SSL_CTX *ctx = ((SSL *) socket->ssl)->ctx;
SSL_CTX_use_certificate_chain_file(ctx, client_cert);
SSL_CTX_use_PrivateKey_file(ctx, client_cert,
SSL_FILETYPE_PEM);
}
}
#elif defined(CONFIG_GNUTLS)
gnutls_transport_set_ptr(*((ssl_t *) socket->ssl),
(gnutls_transport_ptr) socket->fd);
/* TODO: Some certificates fuss. --pasky */
#endif
ret = ssl_do_connect(socket);
switch (ret) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_READ2:
socket->ops->set_state(socket, S_SSL_NEG);
set_handlers(socket->fd, (select_handler_T) ssl_want_read,
NULL, (select_handler_T) dns_exception, socket);
return -1;
case SSL_ERROR_NONE:
#ifdef CONFIG_GNUTLS
if (!get_opt_bool("connection.ssl.cert_verify"))
break;
if (!gnutls_certificate_verify_peers(*((ssl_t *) socket->ssl)))
#endif
break;
default:
if (ret != SSL_ERROR_NONE) {
/* DBG("sslerr %s", gnutls_strerror(ret)); */
socket->no_tls = 1;
}
connect_socket(socket, S_SSL_ERROR);
return -1;
}
return 0;
}
/* Return -1 on error, bytes written on success. */
ssize_t
ssl_write(struct socket *socket, unsigned char *data, int len)
{
ssize_t wr = ssl_do_write(socket, data, len);
if (wr <= 0) {
#ifdef CONFIG_OPENSSL
int err = SSL_get_error(socket->ssl, wr);
#elif defined(CONFIG_GNUTLS)
int err = wr;
#endif
if (err == SSL_ERROR_WANT_WRITE ||
err == SSL_ERROR_WANT_WRITE2) {
return -1;
}
if (!wr) return SOCKET_CANT_WRITE;
if (err == SSL_ERROR_SYSCALL)
return SOCKET_SYSCALL_ERROR;
errno = -S_SSL_ERROR;
return SOCKET_INTERNAL_ERROR;
}
return wr;
}
/* Return -1 on error, rd or success. */
ssize_t
ssl_read(struct socket *socket, unsigned char *data, int len)
{
ssize_t rd = ssl_do_read(socket, data, len);
if (rd <= 0) {
#ifdef CONFIG_OPENSSL
int err = SSL_get_error(socket->ssl, rd);
#elif defined(CONFIG_GNUTLS)
int err = rd;
#endif
#ifdef CONFIG_GNUTLS
if (err == GNUTLS_E_REHANDSHAKE)
return -1;
#endif
if (err == SSL_ERROR_WANT_READ ||
err == SSL_ERROR_WANT_READ2) {
return SOCKET_SSL_WANT_READ;
}
if (!rd) return SOCKET_CANT_READ;
if (err == SSL_ERROR_SYSCALL2)
return SOCKET_SYSCALL_ERROR;
errno = -S_SSL_ERROR;
return SOCKET_INTERNAL_ERROR;
}
return rd;
}
int
ssl_close(struct socket *socket)
{
ssl_do_close(socket);
done_ssl_connection(socket);
return 0;
}