/* SSL socket workshop */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef CONFIG_OPENSSL #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 #include #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 /* Refuse to negotiate TLS 1.0 and later protocols on @socket->ssl. * Without this, connecting to with GnuTLS * 1.3.5 would result in an SSL error. The bug may be in the server * (Netscape-Enterprise/3.6 SP3), in GnuTLS, or in ELinks; please log * your findings to ELinks bug 712. */ 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) { /* GnuTLS does not support SSLv2 because it is "insecure". * That leaves only SSLv3. */ static const int protocol_priority[] = { GNUTLS_SSL3, 0 }; gnutls_protocol_set_priority(*(ssl_t *) socket->ssl, protocol_priority); } #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; }