/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with OpenSSL (or a modified version of that library), containing parts * covered by the terms of OpenSSL License and SSLeay License, the licensors * of this Program grant you additional permission to convey the resulting work. * */ #include "socket.h" #include "jpsock.h" #include "jconf.h" #include "console.h" #include "executor.h" #ifndef CONF_NO_TLS #include #include #include #ifndef OPENSSL_THREADS #error OpenSSL was compiled without thread support #endif #endif plain_socket::plain_socket(jpsock* err_callback) : pCallback(err_callback) { hSocket = INVALID_SOCKET; pSockAddr = nullptr; } bool plain_socket::set_hostname(const char* sAddr) { char sAddrMb[256]; char *sTmp, *sPort; size_t ln = strlen(sAddr); if (ln >= sizeof(sAddrMb)) return pCallback->set_socket_error("CONNECT error: Pool address overflow."); memcpy(sAddrMb, sAddr, ln); sAddrMb[ln] = '\0'; if ((sTmp = strstr(sAddrMb, "//")) != nullptr) memmove(sAddrMb, sTmp, strlen(sTmp) + 1); if ((sPort = strchr(sAddrMb, ':')) == nullptr) return pCallback->set_socket_error("CONNECT error: Pool port number not specified, please use format :."); sPort[0] = '\0'; sPort++; addrinfo hints = { 0 }; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; pAddrRoot = nullptr; int err; if ((err = getaddrinfo(sAddrMb, sPort, &hints, &pAddrRoot)) != 0) return pCallback->set_socket_error_strerr("CONNECT error: GetAddrInfo: ", err); addrinfo *ptr = pAddrRoot; addrinfo *ipv4 = nullptr, *ipv6 = nullptr; while (ptr != nullptr) { if (ptr->ai_family == AF_INET) ipv4 = ptr; if (ptr->ai_family == AF_INET6) ipv6 = ptr; ptr = ptr->ai_next; } if (ipv4 == nullptr && ipv6 == nullptr) { freeaddrinfo(pAddrRoot); pAddrRoot = nullptr; return pCallback->set_socket_error("CONNECT error: I found some DNS records but no IPv4 or IPv6 addresses."); } else if (ipv4 != nullptr && ipv6 == nullptr) pSockAddr = ipv4; else if (ipv4 == nullptr && ipv6 != nullptr) pSockAddr = ipv6; else if (ipv4 != nullptr && ipv6 != nullptr) { if(jconf::inst()->PreferIpv4()) pSockAddr = ipv4; else pSockAddr = ipv6; } hSocket = socket(pSockAddr->ai_family, pSockAddr->ai_socktype, pSockAddr->ai_protocol); if (hSocket == INVALID_SOCKET) { freeaddrinfo(pAddrRoot); pAddrRoot = nullptr; return pCallback->set_socket_error_strerr("CONNECT error: Socket creation failed "); } return true; } bool plain_socket::connect() { int ret = ::connect(hSocket, pSockAddr->ai_addr, (int)pSockAddr->ai_addrlen); freeaddrinfo(pAddrRoot); pAddrRoot = nullptr; if (ret != 0) return pCallback->set_socket_error_strerr("CONNECT error: "); else return true; } int plain_socket::recv(char* buf, unsigned int len) { int ret = ::recv(hSocket, buf, len, 0); if(ret == 0) pCallback->set_socket_error("RECEIVE error: socket closed"); if(ret == SOCKET_ERROR || ret < 0) pCallback->set_socket_error_strerr("RECEIVE error: "); return ret; } bool plain_socket::send(const char* buf) { int pos = 0, slen = strlen(buf); while (pos != slen) { int ret = ::send(hSocket, buf + pos, slen - pos, 0); if (ret == SOCKET_ERROR) { pCallback->set_socket_error_strerr("SEND error: "); return false; } else pos += ret; } return true; } void plain_socket::close(bool free) { if(hSocket != INVALID_SOCKET) { sock_close(hSocket); hSocket = INVALID_SOCKET; } } #ifndef CONF_NO_TLS tls_socket::tls_socket(jpsock* err_callback) : pCallback(err_callback) { } void tls_socket::print_error() { BIO* err_bio = BIO_new(BIO_s_mem()); ERR_print_errors(err_bio); char *buf = nullptr; size_t len = BIO_get_mem_data(err_bio, &buf); pCallback->set_socket_error(buf, len); BIO_free(err_bio); } void tls_socket::init_ctx() { const SSL_METHOD* method = SSLv23_method(); if(method == nullptr) return; ctx = SSL_CTX_new(method); if(ctx == nullptr) return; if(jconf::inst()->TlsSecureAlgos()) { SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_COMPRESSION); } } bool tls_socket::set_hostname(const char* sAddr) { if(ctx == nullptr) { init_ctx(); if(ctx == nullptr) { print_error(); return false; } } if((bio = BIO_new_ssl_connect(ctx)) == nullptr) { print_error(); return false; } if(BIO_set_conn_hostname(bio, sAddr) != 1) { print_error(); return false; } BIO_get_ssl(bio, &ssl); if(ssl == nullptr) { print_error(); return false; } if(jconf::inst()->TlsSecureAlgos()) { if(SSL_set_cipher_list(ssl, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4:!SHA1") != 1) { print_error(); return false; } } return true; } bool tls_socket::connect() { if(BIO_do_connect(bio) != 1) { print_error(); return false; } if(BIO_do_handshake(bio) != 1) { print_error(); return false; } /* Step 1: verify a server certificate was presented during the negotiation */ X509* cert = SSL_get_peer_certificate(ssl); if(cert == nullptr) { print_error(); return false; } const EVP_MD* digest; unsigned char md[EVP_MAX_MD_SIZE]; unsigned int dlen; digest = EVP_get_digestbyname("sha256"); if(digest == nullptr) { print_error(); return false; } if(X509_digest(cert, digest, md, &dlen) != 1) { X509_free(cert); print_error(); return false; } if(pCallback->pool_id != executor::dev_pool_id) { //Base64 encode digest BIO *bmem, *b64; b64 = BIO_new(BIO_f_base64()); bmem = BIO_new(BIO_s_mem()); BIO_puts(bmem, "SHA256:"); b64 = BIO_push(b64, bmem); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_write(b64, md, dlen); BIO_flush(b64); const char* conf_md = jconf::inst()->GetTlsFingerprint(); char *b64_md = nullptr; size_t b64_len = BIO_get_mem_data(bmem, &b64_md); if(strlen(conf_md) == 0) { printer::inst()->print_msg(L1, "Server fingerprint: %.*s", (int)b64_len, b64_md); } else if(strncmp(b64_md, conf_md, b64_len) != 0) { printer::inst()->print_msg(L0, "FINGERPRINT FAILED CHECK: %.*s was given, %s was configured", (int)b64_len, b64_md, conf_md); pCallback->set_socket_error("FINGERPRINT FAILED CHECK"); BIO_free_all(b64); X509_free(cert); return false; } BIO_free_all(b64); } X509_free(cert); return true; } int tls_socket::recv(char* buf, unsigned int len) { int ret = BIO_read(bio, buf, len); if(ret == 0) pCallback->set_socket_error("RECEIVE error: socket closed"); if(ret < 0) print_error(); return ret; } bool tls_socket::send(const char* buf) { return BIO_puts(bio, buf) > 0; } void tls_socket::close(bool free) { if(bio == nullptr || ssl == nullptr) return; if(!free) { sock_close(BIO_get_fd(bio, nullptr)); } else { BIO_free_all(bio); ssl = nullptr; bio = nullptr; } } #endif