BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout) { int status; UINT32 option_value; socklen_t option_len; if (!hostname) return FALSE; if (hostname[0] == '/') tcp->ipcSocket = TRUE; if (tcp->ipcSocket) { tcp->sockfd = freerdp_uds_connect(hostname); if (tcp->sockfd < 0) return FALSE; tcp->socketBio = BIO_new(BIO_s_simple_socket()); if (!tcp->socketBio) return FALSE; BIO_set_fd(tcp->socketBio, tcp->sockfd, BIO_CLOSE); } else { #ifdef HAVE_POLL_H struct pollfd pollfds; #else fd_set cfds; struct timeval tv; #endif #ifdef NO_IPV6 tcp->socketBio = BIO_new(BIO_s_connect()); if (!tcp->socketBio) return FALSE; if (BIO_set_conn_hostname(tcp->socketBio, hostname) < 0 || BIO_set_conn_int_port(tcp->socketBio, &port) < 0) return FALSE; BIO_set_nbio(tcp->socketBio, 1); status = BIO_do_connect(tcp->socketBio); if ((status <= 0) && !BIO_should_retry(tcp->socketBio)) return FALSE; tcp->sockfd = BIO_get_fd(tcp->socketBio, NULL); if (tcp->sockfd < 0) return FALSE; #else /* NO_IPV6 */ struct addrinfo hints = {0}; struct addrinfo *result; struct addrinfo *tmp; char port_str[11]; //ZeroMemory(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_STREAM; /* * FIXME: the following is a nasty workaround. Find a cleaner way: * Either set port manually afterwards or get it passed as string? */ sprintf_s(port_str, 11, "%u", port); status = getaddrinfo(hostname, port_str, &hints, &result); if (status) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return FALSE; } /* For now prefer IPv4 over IPv6. */ tmp = result; if (tmp->ai_family == AF_INET6 && tmp->ai_next != 0) { while ((tmp = tmp->ai_next)) { if (tmp->ai_family == AF_INET) break; } if (!tmp) tmp = result; } tcp->sockfd = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol); if (tcp->sockfd < 0) { freeaddrinfo(result); return FALSE; } if (connect(tcp->sockfd, tmp->ai_addr, tmp->ai_addrlen) < 0) { fprintf(stderr, "connect: %s\n", strerror(errno)); freeaddrinfo(result); return FALSE; } freeaddrinfo(result); tcp->socketBio = BIO_new_socket(tcp->sockfd, BIO_NOCLOSE); /* TODO: make sure the handshake is done by querying the bio */ // if (BIO_should_retry(tcp->socketBio)) // return FALSE; #endif /* NO_IPV6 */ if (status <= 0) { #ifdef HAVE_POLL_H pollfds.fd = tcp->sockfd; pollfds.events = POLLOUT; pollfds.revents = 0; do { status = poll(&pollfds, 1, timeout * 1000); } while ((status < 0) && (errno == EINTR)); #else FD_ZERO(&cfds); FD_SET(tcp->sockfd, &cfds); tv.tv_sec = timeout; tv.tv_usec = 0; status = _select(tcp->sockfd + 1, NULL, &cfds, NULL, &tv); #endif if (status == 0) { return FALSE; /* timeout */ } } (void)BIO_set_close(tcp->socketBio, BIO_NOCLOSE); BIO_free(tcp->socketBio); tcp->socketBio = BIO_new(BIO_s_simple_socket()); if (!tcp->socketBio) return FALSE; BIO_set_fd(tcp->socketBio, tcp->sockfd, BIO_CLOSE); } SetEventFileDescriptor(tcp->event, tcp->sockfd); tcp_get_ip_address(tcp); tcp_get_mac_address(tcp); option_value = 1; option_len = sizeof(option_value); if (!tcp->ipcSocket) { if (setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_NODELAY, (void*) &option_value, option_len) < 0) WLog_ERR(TAG, "unable to set TCP_NODELAY"); } /* receive buffer must be a least 32 K */ if (getsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, &option_len) == 0) { if (option_value < (1024 * 32)) { option_value = 1024 * 32; option_len = sizeof(option_value); if (setsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, option_len) < 0) { WLog_ERR(TAG, "unable to set receive buffer len"); return FALSE; } } } if (!tcp->ipcSocket) { if (!tcp_set_keep_alive_mode(tcp)) return FALSE; } tcp->bufferedBio = BIO_new(BIO_s_buffered_socket()); if (!tcp->bufferedBio) return FALSE; tcp->bufferedBio->ptr = tcp; tcp->bufferedBio = BIO_push(tcp->bufferedBio, tcp->socketBio); return TRUE; }
void *netConnectHttpsThread(void *threadParam) /* use a thread to run socket back to user */ { /* child */ struct netConnectHttpsParams *params = threadParam; pthread_detach(params->thread); // this thread will never join back with it's progenitor int fd=0; char hostnameProto[256]; BIO *sbio; SSL_CTX *ctx; SSL *ssl; openSslInit(); ctx = SSL_CTX_new(SSLv23_client_method()); fd_set readfds; fd_set writefds; int err; struct timeval tv; /* TODO checking certificates char *certFile = NULL; char *certPath = NULL; if (certFile || certPath) { SSL_CTX_load_verify_locations(ctx,certFile,certPath); #if (OPENSSL_VERSION_NUMBER < 0x0090600fL) SSL_CTX_set_verify_depth(ctx,1); #endif } // verify paths and mode. */ sbio = BIO_new_ssl_connect(ctx); BIO_get_ssl(sbio, &ssl); if(!ssl) { xerr("Can't locate SSL pointer"); goto cleanup; } /* Don't want any retries since we are non-blocking bio now */ //SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); safef(hostnameProto,sizeof(hostnameProto),"%s:%d",params->hostName,params->port); BIO_set_conn_hostname(sbio, hostnameProto); BIO_set_nbio(sbio, 1); /* non-blocking mode */ while (1) { if (BIO_do_connect(sbio) == 1) { break; /* Connected */ } if (! BIO_should_retry(sbio)) { xerr("BIO_do_connect() failed"); char s[256]; safef(s, sizeof s, "SSL error: %s", ERR_reason_error_string(ERR_get_error())); xerr(s); goto cleanup; } fd = BIO_get_fd(sbio, NULL); if (fd == -1) { xerr("unable to get BIO descriptor"); goto cleanup; } FD_ZERO(&readfds); FD_ZERO(&writefds); if (BIO_should_read(sbio)) { FD_SET(fd, &readfds); } else if (BIO_should_write(sbio)) { FD_SET(fd, &writefds); } else { /* BIO_should_io_special() */ FD_SET(fd, &readfds); FD_SET(fd, &writefds); } tv.tv_sec = (long) (DEFAULTCONNECTTIMEOUTMSEC/1000); // timeout default 10 seconds tv.tv_usec = (long) (((DEFAULTCONNECTTIMEOUTMSEC/1000)-tv.tv_sec)*1000000); err = select(fd + 1, &readfds, &writefds, NULL, &tv); if (err < 0) { xerr("select() error"); goto cleanup; } if (err == 0) { char s[256]; safef(s, sizeof s, "connection timeout to %s", params->hostName); xerr(s); goto cleanup; } } /* TODO checking certificates if (certFile || certPath) if (!check_cert(ssl, host)) return -1; */ /* we need to wait on both the user's socket and the BIO SSL socket * to see if we need to ferry data from one to the other */ char sbuf[32768]; // socket buffer sv[1] to user char bbuf[32768]; // bio buffer int srd = 0; int swt = 0; int brd = 0; int bwt = 0; while (1) { // Do NOT move this outside the while loop. /* Get underlying file descriptor, needed for select call */ fd = BIO_get_fd(sbio, NULL); if (fd == -1) { xerr("BIO doesn't seem to be initialized in https, unable to get descriptor."); goto cleanup; } FD_ZERO(&readfds); FD_ZERO(&writefds); if (brd == 0) FD_SET(fd, &readfds); if (swt < srd) FD_SET(fd, &writefds); if (srd == 0) FD_SET(params->sv[1], &readfds); tv.tv_sec = (long) (DEFAULTCONNECTTIMEOUTMSEC/1000); // timeout default 10 seconds tv.tv_usec = (long) (((DEFAULTCONNECTTIMEOUTMSEC/1000)-tv.tv_sec)*1000000); err = select(max(fd,params->sv[1]) + 1, &readfds, &writefds, NULL, &tv); /* Evaluate select() return code */ if (err < 0) { xerr("error during select()"); goto cleanup; } else if (err == 0) { /* Timed out - just quit */ xerr("https timeout expired"); goto cleanup; } else { if (FD_ISSET(params->sv[1], &readfds)) { swt = 0; srd = read(params->sv[1], sbuf, 32768); if (srd == -1) { if (errno != 104) // udcCache often closes causing "Connection reset by peer" xerrno("error reading https socket"); goto cleanup; } if (srd == 0) break; // user closed socket, we are done } if (FD_ISSET(fd, &writefds)) { int swtx = BIO_write(sbio, sbuf+swt, srd-swt); if (swtx <= 0) { if (!BIO_should_write(sbio)) { ERR_print_errors_fp(stderr); xerr("Error writing SSL connection"); goto cleanup; } } else { swt += swtx; if (swt >= srd) { swt = 0; srd = 0; } } } if (FD_ISSET(fd, &readfds)) { bwt = 0; brd = BIO_read(sbio, bbuf, 32768); if (brd <= 0) { if (BIO_should_read(sbio)) { brd = 0; continue; } else { if (brd == 0) break; ERR_print_errors_fp(stderr); xerr("Error reading SSL connection"); goto cleanup; } } // write the https data received immediately back on socket to user, and it's ok if it blocks. while(bwt < brd) { int bwtx = write(params->sv[1], bbuf+bwt, brd-bwt); if (bwtx == -1) { if ((errno != 104) // udcCache often closes causing "Connection reset by peer" && (errno != 32)) // udcCache often closes causing "Broken pipe" xerrno("error writing https data back to user socket"); goto cleanup; } bwt += bwtx; } brd = 0; bwt = 0; } } } cleanup: BIO_free_all(sbio); close(params->sv[1]); /* we are done with it */ return NULL; }
netsnmp_transport * netsnmp_tlstcp_open(netsnmp_transport *t) { _netsnmpTLSBaseData *tlsdata; BIO *bio; SSL_CTX *ctx; SSL *ssl; int rc = 0; _netsnmp_verify_info *verify_info; netsnmp_assert_or_return(t != NULL, NULL); netsnmp_assert_or_return(t->data != NULL, NULL); netsnmp_assert_or_return(sizeof(_netsnmpTLSBaseData) == t->data_length, NULL); tlsdata = t->data; if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) { /* Is the client */ /* RFC5953 Section 5.3.1: Establishing a Session as a Client * 1) The snmpTlstmSessionOpens counter is incremented. */ snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS); /* RFC5953 Section 5.3.1: Establishing a Session as a Client 2) The client selects the appropriate certificate and cipher_suites for the key agreement based on the tmSecurityName and the tmRequestedSecurityLevel for the session. For sessions being established as a result of a SNMP-TARGET-MIB based operation, the certificate will potentially have been identified via the snmpTlstmParamsTable mapping and the cipher_suites will have to be taken from system-wide or implementation-specific configuration. If no row in the snmpTlstmParamsTable exists then implementations MAY choose to establish the connection using a default client certificate available to the application. Otherwise, the certificate and appropriate cipher_suites will need to be passed to the openSession() ASI as supplemental information or configured through an implementation-dependent mechanism. It is also implementation-dependent and possibly policy-dependent how tmRequestedSecurityLevel will be used to influence the security capabilities provided by the (D)TLS connection. However this is done, the security capabilities provided by (D)TLS MUST be at least as high as the level of security indicated by the tmRequestedSecurityLevel parameter. The actual security level of the session is reported in the tmStateReference cache as tmSecurityLevel. For (D)TLS to provide strong authentication, each principal acting as a command generator SHOULD have its own certificate. */ /* Implementation notes: we do most of this in the sslctx_client_setup The transport should have been f_config()ed with the proper fingerprints to use (which is stored in tlsdata), or we'll use the default identity fingerprint if that can be found. */ /* XXX: check securityLevel and ensure no NULL fingerprints are used */ /* set up the needed SSL context */ tlsdata->ssl_context = ctx = sslctx_client_setup(TLSv1_method(), tlsdata); if (!ctx) { snmp_log(LOG_ERR, "failed to create TLS context\n"); return NULL; } /* RFC5953 Section 5.3.1: Establishing a Session as a Client 3) Using the destTransportDomain and destTransportAddress values, the client will initiate the (D)TLS handshake protocol to establish session keys for message integrity and encryption. */ /* Implementation note: The transport domain and address are pre-processed by this point */ /* Create a BIO connection for it */ DEBUGMSGTL(("tlstcp", "connecting to tlstcp %s\n", tlsdata->addr_string)); t->remote = (void *) strdup(tlsdata->addr_string); t->remote_length = strlen(tlsdata->addr_string) + 1; bio = BIO_new_connect(tlsdata->addr_string); /* RFC5953 Section 5.3.1: Establishing a Session as a Client 3) continued: If the attempt to establish a session is unsuccessful, then snmpTlstmSessionOpenErrors is incremented, an error indication is returned, and processing stops. */ if (NULL == bio) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to create bio\n"); _openssl_log_error(rc, NULL, "BIO creation"); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* Tell the BIO to actually do the connection */ if ((rc = BIO_do_connect(bio)) <= 0) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to connect to %s\n", tlsdata->addr_string); _openssl_log_error(rc, NULL, "BIO_do_connect"); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* Create the SSL layer on top of the socket bio */ ssl = tlsdata->ssl = SSL_new(ctx); if (NULL == ssl) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n"); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* Bind the SSL layer to the BIO */ SSL_set_bio(ssl, bio, bio); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); verify_info = SNMP_MALLOC_TYPEDEF(_netsnmp_verify_info); if (NULL == verify_info) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n"); SSL_shutdown(ssl); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } SSL_set_ex_data(ssl, tls_get_verify_info_index(), verify_info); /* Then have SSL do it's connection over the BIO */ if ((rc = SSL_connect(ssl)) <= 0) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to ssl_connect\n"); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* RFC5953 Section 5.3.1: Establishing a Session as a Client 3) continued: If the session failed to open because the presented server certificate was unknown or invalid then the snmpTlstmSessionUnknownServerCertificate or snmpTlstmSessionInvalidServerCertificates MUST be incremented and a snmpTlstmServerCertificateUnknown or snmpTlstmServerInvalidCertificate notification SHOULD be sent as appropriate. Reasons for server certificate invalidation includes, but is not limited to, cryptographic validation failures and an unexpected presented certificate identity. */ /* RFC5953 Section 5.3.1: Establishing a Session as a Client 4) The (D)TLS client MUST then verify that the (D)TLS server's presented certificate is the expected certificate. The (D)TLS client MUST NOT transmit SNMP messages until the server certificate has been authenticated, the client certificate has been transmitted and the TLS connection has been fully established. If the connection is being established from configuration based on SNMP-TARGET-MIB configuration, then the snmpTlstmAddrTable DESCRIPTION clause describes how the verification is done (using either a certificate fingerprint, or an identity authenticated via certification path validation). If the connection is being established for reasons other than configuration found in the SNMP-TARGET-MIB then configuration and procedures outside the scope of this document should be followed. Configuration mechanisms SHOULD be similar in nature to those defined in the snmpTlstmAddrTable to ensure consistency across management configuration systems. For example, a command-line tool for generating SNMP GETs might support specifying either the server's certificate fingerprint or the expected host name as a command line argument. */ /* Implementation notes: - All remote certificate fingerprints are expected to be stored in the transport's config information. This is true both for CLI clients and TARGET-MIB sessions. - netsnmp_tlsbase_verify_server_cert implements these checks */ if (netsnmp_tlsbase_verify_server_cert(ssl, tlsdata) != SNMPERR_SUCCESS) { /* XXX: unknown vs invalid; two counters */ snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE); snmp_log(LOG_ERR, "tlstcp: failed to verify ssl certificate\n"); SSL_shutdown(ssl); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* RFC5953 Section 5.3.1: Establishing a Session as a Client 5) (D)TLS provides assurance that the authenticated identity has been signed by a trusted configured certification authority. If verification of the server's certificate fails in any way (for example because of failures in cryptographic verification or the presented identity did not match the expected named entity) then the session establishment MUST fail, the snmpTlstmSessionInvalidServerCertificates object is incremented. If the session can not be opened for any reason at all, including cryptographic verification failures, then the snmpTlstmSessionOpenErrors counter is incremented and processing stops. */ /* XXX: add snmpTlstmSessionInvalidServerCertificates on crypto failure */ /* RFC5953 Section 5.3.1: Establishing a Session as a Client 6) The TLSTM-specific session identifier (tlstmSessionID) is set in the tmSessionID of the tmStateReference passed to the TLS Transport Model to indicate that the session has been established successfully and to point to a specific (D)TLS connection for future use. The tlstmSessionID is also stored in the LCD for later lookup during processing of incoming messages (Section 5.1.2). */ /* Implementation notes: - the tlsdata pointer is used as our session identifier, as noted in the netsnmp_tlstcp_recv() function comments. */ t->sock = BIO_get_fd(bio, NULL); } else { /* Is the server */ /* Create the socket bio */ DEBUGMSGTL(("tlstcp", "listening on tlstcp port %s\n", tlsdata->addr_string)); tlsdata->accept_bio = BIO_new_accept(tlsdata->addr_string); t->local = (void *) strdup(tlsdata->addr_string); t->local_length = strlen(tlsdata->addr_string)+1; if (NULL == tlsdata->accept_bio) { SNMP_FREE(t); SNMP_FREE(tlsdata); snmp_log(LOG_ERR, "TLSTCP: Falied to create a accept BIO\n"); return NULL; } /* openssl requires an initial accept to bind() the socket */ if (BIO_do_accept(tlsdata->accept_bio) <= 0) { _openssl_log_error(rc, tlsdata->ssl, "BIO_do__accept"); SNMP_FREE(t); SNMP_FREE(tlsdata); snmp_log(LOG_ERR, "TLSTCP: Falied to do first accept on the TLS accept BIO\n"); return NULL; } /* create the OpenSSL TLS context */ tlsdata->ssl_context = sslctx_server_setup(TLSv1_method()); t->sock = BIO_get_fd(tlsdata->accept_bio, NULL); t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; } return t; }
/** * oh_ssl_connect * @hostname: Name of target host. Format: * "hostname:port" or "IPaddress:port" * @ctx: pointer to SSL_CTX as returned by oh_ssl_ctx_init() * @timeout: maximum number of seconds to wait for a connection to * hostname, or zero to wait forever * * Create and open a new ssl conection to the specified host. * * Return value: pointer to BIO, or NULL for failure **/ BIO *oh_ssl_connect(char *hostname, SSL_CTX *ctx, long timeout) { BIO *bio; SSL *ssl; fd_set readfds; fd_set writefds; struct timeval tv; int fd; int err; if (hostname == NULL) { err("NULL hostname in oh_ssl_connect()"); return(NULL); } if (ctx == NULL) { err("NULL ctx in oh_ssl_connect()"); return(NULL); } if (timeout < 0) { err("inappropriate timeout in oh_ssl_connect()"); return(NULL); } /* Start with a new SSL BIO */ bio = BIO_new_ssl_connect(ctx); if (bio == NULL) { err("BIO_new_ssl_connect() failed"); return(NULL); } /* Set up connection parameters for this BIO */ BIO_set_conn_hostname(bio, hostname); BIO_set_nbio(bio, 1); /* Set underlying socket to * non-blocking mode */ /* Set up SSL session parameters */ BIO_get_ssl(bio, &ssl); if (ssl == NULL) { err("BIO_get_ssl() failed"); BIO_free_all(bio); return(NULL); } SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); /* Ready to open the connection. Note that this call will probably * take a while, so we need to retry it, watching for a timeout. */ while (1) { if (BIO_do_connect(bio) == 1) { break; /* Connection established */ } if (! BIO_should_retry(bio)) { /* Hard error? */ err("BIO_do_connect() failed"); err("SSL error: %s", ERR_reason_error_string(ERR_get_error())); BIO_free_all(bio); return(NULL); } /* Wait until there's a change in the socket's status or until * the timeout period. * * Get underlying file descriptor, needed for the select call. */ fd = BIO_get_fd(bio, NULL); if (fd == -1) { err("BIO isn't initialized in oh_ssl_connect()"); BIO_free_all(bio); return(NULL); } FD_ZERO(&readfds); FD_ZERO(&writefds); if (BIO_should_read(bio)) { FD_SET(fd, &readfds); } else if (BIO_should_write(bio)) { FD_SET(fd, &writefds); } else { /* This is BIO_should_io_special(). * Not sure what "special" needs to * wait for, but both read and write * seems to work without unnecessary * retries. */ FD_SET(fd, &readfds); FD_SET(fd, &writefds); } if (timeout) { tv.tv_sec = timeout; tv.tv_usec = 0; err = select(fd + 1, &readfds, &writefds, NULL, &tv); } else { /* No timeout */ err = select(fd + 1, &readfds, &writefds, NULL, NULL); } /* Evaluate select() return code */ if (err < 0) { err("error during select()"); BIO_free_all(bio); return(NULL); } if (err == 0) { err("connection timeout to %s", hostname); BIO_free_all(bio); return(NULL); /* Timeout */ } } /* TODO: Do I need to set the client or server mode here? I don't * think so. */ return(bio); }
int main(int argc, char **argv) { BIO *sbio = NULL, *out = NULL; int i, len, rv; char tmpbuf[1024]; SSL_CTX *ctx = NULL; SSL_CONF_CTX *cctx = NULL; SSL *ssl = NULL; CONF *conf = NULL; STACK_OF(CONF_VALUE) *sect = NULL; CONF_VALUE *cnf; const char *connect_str = "localhost:4433"; long errline = -1; ERR_load_crypto_strings(); ERR_load_SSL_strings(); SSL_library_init(); conf = NCONF_new(NULL); if (NCONF_load(conf, "connect.cnf", &errline) <= 0) { if (errline <= 0) fprintf(stderr, "Error processing config file\n"); else fprintf(stderr, "Error on line %ld\n", errline); goto end; } sect = NCONF_get_section(conf, "default"); if (sect == NULL) { fprintf(stderr, "Error retrieving default section\n"); goto end; } ctx = SSL_CTX_new(SSLv23_client_method()); cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); for (i = 0; i < sk_CONF_VALUE_num(sect); i++) { cnf = sk_CONF_VALUE_value(sect, i); rv = SSL_CONF_cmd(cctx, cnf->name, cnf->value); if (rv > 0) continue; if (rv != -2) { fprintf(stderr, "Error processing %s = %s\n", cnf->name, cnf->value); ERR_print_errors_fp(stderr); goto end; } if (!strcmp(cnf->name, "Connect")) { connect_str = cnf->value; } else { fprintf(stderr, "Unknown configuration option %s\n", cnf->name); goto end; } } if (!SSL_CONF_CTX_finish(cctx)) { fprintf(stderr, "Finish error\n"); ERR_print_errors_fp(stderr); goto err; } /* * We'd normally set some stuff like the verify paths and * mode here * because as things stand this will connect to * any server whose * certificate is signed by any CA. */ sbio = BIO_new_ssl_connect(ctx); BIO_get_ssl(sbio, &ssl); if (!ssl) { fprintf(stderr, "Can't locate SSL pointer\n"); goto end; } /* Don't want any retries */ SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* We might want to do other things with ssl here */ BIO_set_conn_hostname(sbio, connect_str); out = BIO_new_fp(stdout, BIO_NOCLOSE); if (BIO_do_connect(sbio) <= 0) { fprintf(stderr, "Error connecting to server\n"); ERR_print_errors_fp(stderr); goto end; } if (BIO_do_handshake(sbio) <= 0) { fprintf(stderr, "Error establishing SSL connection\n"); ERR_print_errors_fp(stderr); goto end; } /* Could examine ssl here to get connection info */ BIO_puts(sbio, "GET / HTTP/1.0\n\n"); for (;;) { len = BIO_read(sbio, tmpbuf, 1024); if (len <= 0) break; BIO_write(out, tmpbuf, len); } end: SSL_CONF_CTX_free(cctx); BIO_free_all(sbio); BIO_free(out); NCONF_free(conf); return 0; }
struct dyString *bio(char *host, char *url, char *certFile, char *certPath) /* This SSL/TLS client example, attempts to retrieve a page from an SSL/TLS web server. The I/O routines are identical to those of the unencrypted example in BIO_s_connect(3). */ { struct dyString *dy = dyStringNew(0); char hostnameProto[256]; char requestLine[4096]; BIO *sbio; int len; char tmpbuf[1024]; SSL_CTX *ctx; SSL *ssl; SSL_library_init(); ERR_load_crypto_strings(); ERR_load_SSL_strings(); OpenSSL_add_all_algorithms(); /* We would seed the PRNG here if the platform didn't * do it automatically */ ctx = SSL_CTX_new(SSLv23_client_method()); if (certFile || certPath) { SSL_CTX_load_verify_locations(ctx,certFile,certPath); #if (OPENSSL_VERSION_NUMBER < 0x0090600fL) SSL_CTX_set_verify_depth(ctx,1); #endif } /* We'd normally set some stuff like the verify paths and * mode here because as things stand this will connect to * any server whose certificate is signed by any CA. */ sbio = BIO_new_ssl_connect(ctx); BIO_get_ssl(sbio, &ssl); if(!ssl) { fprintf(stderr, "Can't locate SSL pointer\n"); return NULL; } /* Don't want any retries */ SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* We might want to do other things with ssl here */ safef(hostnameProto,sizeof(hostnameProto),"%s:https",host); BIO_set_conn_hostname(sbio, hostnameProto); if(BIO_do_connect(sbio) <= 0) { fprintf(stderr, "Error connecting to server\n"); ERR_print_errors_fp(stderr); return NULL; } if(BIO_do_handshake(sbio) <= 0) { fprintf(stderr, "Error establishing SSL connection\n"); ERR_print_errors_fp(stderr); return NULL; } if (certFile || certPath) if (!check_cert(ssl, host)) return NULL; /* Could examine ssl here to get connection info */ safef(requestLine,sizeof(requestLine),"GET %s HTTP/1.0\n\n",url); BIO_puts(sbio, requestLine); for(;;) { len = BIO_read(sbio, tmpbuf, 1024); if(len <= 0) break; dyStringAppendN(dy, tmpbuf, len); } BIO_free_all(sbio); return dy; }
int proxy_null(BIO **cbio, BIO **sbio, char *header) { char data[READ_BUFF_SIZE]; int len, written; char *host, *p; /* retrieve the host tag */ host = strcasestr(header, HTTP_HOST_TAG); if (host == NULL) return -EINVALID; SAFE_STRDUP(host, host + strlen(HTTP_HOST_TAG)); /* trim the eol */ if ((p = strchr(host, '\r')) != NULL) *p = 0; if ((p = strchr(host, '\n')) != NULL) *p = 0; /* connect to the real server */ *sbio = BIO_new(BIO_s_connect()); BIO_set_conn_hostname(*sbio, host); BIO_set_conn_port(*sbio, "http"); if (BIO_do_connect(*sbio) <= 0) { DEBUG_MSG(D_ERROR, "Cannot connect to [%s]", host); SAFE_FREE(host); return -ENOADDRESS; } DEBUG_MSG(D_INFO, "Connection to [%s]", host); /* * sanitize the header to avoid strange reply from the server. * we don't want to cope with keep-alive !!! */ sanitize_header(header); /* send the request to the server */ BIO_puts(*sbio, header); memset(data, 0, sizeof(data)); written = 0; /* read the reply header from the server */ LOOP { len = BIO_read(*sbio, data + written, sizeof(char)); if (len <= 0) break; written += len; if (strstr(data, CR LF CR LF) || strstr(data, LF LF)) break; } /* send the headers to the client, the data will be sent in the callee function */ BIO_write(*cbio, data, written); SAFE_FREE(host); return ESUCCESS; }
//-------------------------------------------------- // sends an OCSP_REQUES object to remore server and // retrieves the OCSP_RESPONSE object // resp - buffer to store the new responses pointer // req - request objects pointer // url - OCSP responder URL //-------------------------------------------------- int ddocPullUrl(const char* url, DigiDocMemBuf* pSendData, DigiDocMemBuf* pRecvData, const char* proxyHost, const char* proxyPort) { BIO* cbio = 0, *sbio = 0; SSL_CTX *ctx = NULL; char *host = NULL, *port = NULL, *path = "/", buf[200]; int err = ERR_OK, use_ssl = -1, rc; long e; //RETURN_IF_NULL_PARAM(pSendData); // may be null if nothing to send? RETURN_IF_NULL_PARAM(pRecvData); RETURN_IF_NULL_PARAM(url); ddocDebug(3, "ddocPullUrl", "URL: %s, in: %d bytes", url, pSendData->nLen); //there is an HTTP proxy - connect to that instead of the target host if (proxyHost != 0 && *proxyHost != '\0') { host = (char*)proxyHost; if(proxyPort != 0 && *proxyPort != '\0') port = (char*)proxyPort; path = (char*)url; } else { if(OCSP_parse_url((char*)url, &host, &port, &path, &use_ssl) == 0) { ddocDebug(1, "ddocPullUrl", "Failed to parse the URL"); return ERR_WRONG_URL_OR_PROXY; } } if((cbio = BIO_new_connect(host)) != 0) { if(port != NULL) BIO_set_conn_port(cbio, port); if(use_ssl == 1) { ctx = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); sbio = BIO_new_ssl(ctx, 1); cbio = BIO_push(sbio, cbio); } if ((rc = BIO_do_connect(cbio)) > 0) { ddocDebug(4, "ddocPullUrl", "Connected: %d", rc); if(pSendData && pSendData->nLen && pSendData->pMem) { rc = BIO_write(cbio, pSendData->pMem, pSendData->nLen); ddocDebug(4, "ddocPullUrl", "Sent: %d bytes, got: %d", pSendData->nLen, rc); } do { memset(buf, 0, sizeof(buf)); rc = BIO_read(cbio, buf, sizeof(buf)-1); ddocDebug(4, "ddocPullUrl", "Received: %d bytes\n", rc); if(rc > 0) err = ddocMemAppendData(pRecvData, buf, rc); } while(rc > 0); ddocDebug(4, "ddocPullUrl", "Total received: %d bytes\n", pRecvData->nLen); } else { //if no connection e = checkErrors(); if(ERR_GET_REASON(e) == BIO_R_BAD_HOSTNAME_LOOKUP || ERR_GET_REASON(e) == OCSP_R_SERVER_WRITE_ERROR) err = ERR_CONNECTION_FAILURE; else err = (host != NULL) ? ERR_WRONG_URL_OR_PROXY : ERR_CONNECTION_FAILURE; } BIO_free_all(cbio); if (use_ssl != -1) { OPENSSL_free(host); OPENSSL_free(port); OPENSSL_free(path); SSL_CTX_free(ctx); } } else err = ERR_CONNECTION_FAILURE; return(err); }
int ssl_transport_insecure_send(option_block *opts, void *d, int i) { FILE *log = stdout; char spec[2048] = {0}; struct timeval tv; int sockfd; fd_set fds; unsigned long int to = MAX(100, opts->time_out); int ret; if(opts->fp_log) log = opts->fp_log; if(ssl_bio == NULL) { snprintf(spec, 2048, "%s:%d", opts->host_spec, opts->port); ssl_bio = BIO_new_connect(spec); if(ssl_bio == NULL) { fprintf(stderr, "<ssl_transport:i-send> failure to acquire BIO: [%s]\n", spec); return -1; } if(BIO_do_connect(ssl_bio) <= 0) { fprintf(stderr, "<ssl_transport:i-send> failure to simple connect to: [%s]\n", spec); return -1; } } retx: if(BIO_write(ssl_bio, d, i) <= 0) { if(!BIO_should_retry(ssl_bio)) { fprintf(stderr, "<ssl_transport:i-send> failure to transmit!\n"); ssl_transport_close(); } goto retx; } if(opts->verbosity != QUIET) fprintf(log, "[%s] <ssl_transport:send> tx fuzz - scanning for reply.\n", get_time_as_log()); BIO_get_fd(ssl_bio, &sockfd); FD_ZERO(&fds); FD_SET(sockfd, &fds); tv.tv_sec = to / 1000; tv.tv_usec = (to % 1000) * 1000; /*time out*/ ret = select(sockfd+1, &fds, NULL, NULL, &tv); if(ret > 0) { if(FD_ISSET(sockfd, &fds)) { char buf[8192] = {0}; int r_len = 0; ret = BIO_read(ssl_bio, buf, 8192); if(ret == 0) { fprintf(stderr, "<ssl_transport:send> remote closed xon\n"); ssl_transport_close(); } else if(ret > 0) { if(opts->verbosity != QUIET) fprintf(log, "[%s] read:\n%s\n===============================================================================\n", get_time_as_log(), buf); } } } if((opts->close_conn) || ((opts->close_conn) && (!opts->forget_conn))) { ssl_transport_close(); } mssleep(opts->reqw_inms); return 0; }
int redisContextConnectSSL(redisContext *c, const char *addr, int port, char* certfile, char* certdir, struct timeval *timeout) { struct timeval start_time; int has_timeout = 0; int is_nonblocking = 0; c->ssl.sd = -1; c->ssl.ctx = NULL; c->ssl.ssl = NULL; c->ssl.bio = NULL; // Set up a SSL_CTX object, which will tell our BIO object how to do its work SSL_CTX* ctx = SSL_CTX_new(TLSv1_1_client_method()); c->ssl.ctx = ctx; // Create a SSL object pointer, which our BIO object will provide. SSL* ssl; // Create our BIO object for SSL connections. BIO* bio = BIO_new_ssl_connect(ctx); c->ssl.bio = bio; // Failure? if (bio == NULL) { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error creating BIO!\n"); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); // We need to free up the SSL_CTX before we leave. cleanupSSL( &c->ssl ); return REDIS_ERR; } // Makes ssl point to bio's SSL object. BIO_get_ssl(bio, &ssl); c->ssl.ssl = ssl; // Set the SSL to automatically retry on failure. SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); char* connect_str = (char *)calloc( 1, strlen( addr ) + 10 ); sprintf( connect_str, "%s:%d", addr, port ); c->ssl.conn_str = connect_str; // We're connection to the REDIS server at host:port BIO_set_conn_hostname(bio, connect_str); SSL_CTX_load_verify_locations(ctx, certfile, certdir); // Are we supposed to be blocking or non-blocking? if( (c->flags & REDIS_BLOCK) == 0) { is_nonblocking = 1; BIO_set_nbio(bio,1); } // What about a timeout? // If we're blocking and have a timeout, we need to handle that specially if( NULL != timeout ) { BIO_set_nbio(bio,1); has_timeout = 1; gettimeofday(&start_time, NULL); } while(1) { struct timeval cur_time, elapsed_time; if (has_timeout) { gettimeofday(&cur_time, NULL); elapsed_time = subtractTimeval( cur_time, start_time ); if (compareTimeval( elapsed_time, *timeout) > 0) { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out."); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } // Same as before, try to connect. if (BIO_do_connect(bio) <= 0 ) { if( BIO_should_retry( bio ) ) { // We need to retry. } else { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Failed to connect"); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } else { // connect is done... break; } if( has_timeout ) { // Do select and seelct on it int result; int fd = BIO_get_fd( bio, NULL ); struct timeval time_left; if (has_timeout) { time_left = subtractTimeval( *timeout, elapsed_time ); } fd_set readset, writeset; FD_ZERO(&readset); FD_SET(fd, &readset); FD_ZERO(&writeset); FD_SET(fd, &writeset); do { result = select( fd+1, &readset, &writeset, NULL, &time_left); } while( result == -1 && errno == EINTR ); } } while(1) { struct timeval cur_time, elapsed_time; if (has_timeout) { gettimeofday(&cur_time, NULL); elapsed_time = subtractTimeval( cur_time, start_time ); if (compareTimeval( elapsed_time, *timeout) > 0) { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out."); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } // Now we need to do the SSL handshake, so we can communicate. if (BIO_do_handshake(bio) <= 0) { if( BIO_should_retry( bio ) ) { // We need to retry. } else { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: handshake failure"); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } else { // handshake is done... break; } if( has_timeout ) { // Do select and seelct on it int result; int fd = BIO_get_fd( bio, NULL ); struct timeval time_left; if (has_timeout) { time_left = subtractTimeval( *timeout, elapsed_time ); } fd_set readset, writeset; FD_ZERO(&readset); FD_SET(fd, &readset); FD_ZERO(&writeset); FD_SET(fd, &writeset); do { result = select( fd+1, &readset, &writeset, NULL, &time_left); } while( result == -1 && errno == EINTR ); } } long verify_result = SSL_get_verify_result(ssl); if( verify_result == X509_V_OK) { X509* peerCertificate = SSL_get_peer_certificate(ssl); char commonName [512]; X509_NAME * name = X509_get_subject_name(peerCertificate); X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512); // TODO: Add a redis config parameter for the common name to check for. // Since Redis connections are generally done with an IP address directly, // we can't just assume that the name we get on the addr is an actual name. // // if(strcasecmp(commonName, "BradBroerman") != 0) { // __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error validating cert common name.\n\n" ); // cleanupSSL( &(c->ssl) ); // return REDIS_ERR; } else { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error retrieving peer certificate.\n" ); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } BIO_set_nbio(bio,is_nonblocking); return REDIS_OK; }
int main(int argc, char **argv) { BIO *sbio = NULL, *out = NULL; int len; char tmpbuf[1024]; SSL_CTX *ctx; SSL_CONF_CTX *cctx; SSL *ssl; char **args = argv + 1; const char *connect_str = "localhost:4433"; int nargs = argc - 1; ERR_load_crypto_strings(); ERR_load_SSL_strings(); SSL_library_init(); ctx = SSL_CTX_new(TLS_client_method()); cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); while (*args && **args == '-') { int rv; /* Parse standard arguments */ rv = SSL_CONF_cmd_argv(cctx, &nargs, &args); if (rv == -3) { fprintf(stderr, "Missing argument for %s\n", *args); goto end; } if (rv < 0) { fprintf(stderr, "Error in command %s\n", *args); ERR_print_errors_fp(stderr); goto end; } /* If rv > 0 we processed something so proceed to next arg */ if (rv > 0) continue; /* Otherwise application specific argument processing */ if (strcmp(*args, "-connect") == 0) { connect_str = args[1]; if (connect_str == NULL) { fprintf(stderr, "Missing -connect argument\n"); goto end; } args += 2; nargs -= 2; continue; } else { fprintf(stderr, "Unknown argument %s\n", *args); goto end; } } if (!SSL_CONF_CTX_finish(cctx)) { fprintf(stderr, "Finish error\n"); ERR_print_errors_fp(stderr); goto end; } /* * We'd normally set some stuff like the verify paths and * mode here * because as things stand this will connect to * any server whose * certificate is signed by any CA. */ sbio = BIO_new_ssl_connect(ctx); BIO_get_ssl(sbio, &ssl); if (!ssl) { fprintf(stderr, "Can't locate SSL pointer\n"); goto end; } /* Don't want any retries */ SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* We might want to do other things with ssl here */ BIO_set_conn_hostname(sbio, connect_str); out = BIO_new_fp(stdout, BIO_NOCLOSE); if (BIO_do_connect(sbio) <= 0) { fprintf(stderr, "Error connecting to server\n"); ERR_print_errors_fp(stderr); goto end; } if (BIO_do_handshake(sbio) <= 0) { fprintf(stderr, "Error establishing SSL connection\n"); ERR_print_errors_fp(stderr); goto end; } /* Could examine ssl here to get connection info */ BIO_puts(sbio, "GET / HTTP/1.0\n\n"); for (;;) { len = BIO_read(sbio, tmpbuf, 1024); if (len <= 0) break; BIO_write(out, tmpbuf, len); } end: SSL_CONF_CTX_free(cctx); BIO_free_all(sbio); BIO_free(out); return 0; }
int main(int argc, char** argv){ int sock = 0; // declaración del socket e inicializado a 0 int error = 0; /** declaramos una variable que nos servirá para detectar * errores */ int serverTalk = 0; //socklen_t length = (socklen_t) sizeof (struct sockaddr_in); // tamaño del paquete //struct sockaddr_in addr; // definimos el contenedor de la dirección unsigned int port = 5678; /** creamos la variable que identifica el puerto * de conexión, siendo el puerto por defecto 5678 */ char dir[DIM] = "localhost"; /** definimos la cadena que contendrá a la * dirección del servidor, la cual, será por * defecto localhost */ //struct hostent* server; // estructura utilizada para la gestión de direcciones sms auxMsj; char name[DIM] = {0}; int nbytes = 0; // contador de bytes leidos y escritos char aux[DIM] = {0}; // inicializamos las variables de SSL BIO* bio = NULL; SSL_CTX* ctx = NULL; SSL* ssl = NULL; char cert[DIM] = "/usr/share/doc/libssl-dev/demos/sign/cert.pem"; //analizamos los parámetros de entrada int i = 0; for(; i < argc; i++){ if(strcmp(argv[i], "-p") == 0){ // leemos el puerto if(argc <= i + 1 || isNum(argv[i+1]) == 0){ perror("Se esperaba un número después de -p"); exit(-1); }else{ PDEBUG("INFO: Puerto identificado\n"); i++; port = atoi(argv[i]); } continue; }else if(strcmp(argv[i], "-d") == 0){ // dirección de destino if(argc <= i + 1){ perror("Se esperaba una dirección después de -d"); exit(-1); }else{ PDEBUG("INFO: Destino identificado"); i++; strcpy(dir, argv[i]); } continue; }else if(strcmp(argv[i], "-cert") == 0){ // dirección de destino if(argc <= i + 1){ perror("Se esperaba una ruta de certificado después de -cert"); exit(-1); }else{ PDEBUG("INFO: Destino identificado"); i++; strcpy(cert, argv[i]); } continue; } } /***********************************SSL************************************/ PDEBUG("INFO: Iniciamos la librería SSL\n"); SSL_load_error_strings(); // strings de error SSL_library_init(); // iniciams la libreria en sí ERR_load_BIO_strings(); // strings de error de BIO OpenSSL_add_all_algorithms(); // inicializamos los algoritmos de la librería PDEBUG("INFO: Conectando...\n"); PDEBUG("INFO: Inicializando los punteros\n"); ctx = SSL_CTX_new(SSLv23_client_method()); ssl = NULL; PDEBUG("INFO: Cargamos el certificado\n"); if(SSL_CTX_load_verify_locations(ctx, cert, NULL) == 0){ char aux[] = "ERROR: No se pudo comprobar el certificado\n"; write(WRITE, aux, strlen(aux)); exit(-1); } PDEBUG("INFO: Inicializando BIO\n"); bio = BIO_new_ssl_connect(ctx); BIO_get_ssl(bio, &ssl); if(ssl == 0){ char aux[] = "ERROR: Error al crear el objeto ssl\n"; write(WRITE, aux, strlen(aux)); exit(-1); } PDEBUG("INFO: Estableciendo el modo de trabajo, no queremos reintentos\n"); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); PDEBUG("INFO: Intentando realizar la conexión\n"); PDEBUG("INFO: Conectando a -> "); sprintf(aux, "%s:%i", dir, port); PDEBUG(aux);PDEBUG("\n"); BIO_set_conn_hostname(bio, aux); PDEBUG("INFO: Verificando la conexión\n"); if (BIO_do_connect(bio) < 1){ char aux[] = "ERROR: al conectar el BIO\n"; write(WRITE, aux, strlen(aux)); exit(-1); } //PDEBUG("INFO: Verificando el resultado de la conexión\n"); //printf("--%i-%i--\n", X509_V_OK, SSL_get_verify_result(ssl)); //if (SSL_get_verify_result(ssl) != X509_V_OK) { // char aux[] = "ERROR: verificar el resultado de la conexión\n"; // write(WRITE, aux, strlen(aux)); // exit(-1); //} PDEBUG("INFO: Conectado\n"); /***********************************SSL************************************/ ////Creamos el socket //PDEBUG("INFO: Creando el socket\n"); //sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); ////Comprobamos si ha ocurrido un error al crear el socket //if(sock < 0){ // char aux[] = "ERROR: creación del socket {{socket()}}:\n"; // write(WRITE, aux, strlen(aux)); // exit(-1); //} // // //addr.sin_family = AF_INET; // familia AF_INET //addr.sin_port = htons(port); // definimos el puerto de conexión //PDEBUG("INFO: Traducimos la dirección del servidor\n"); //server = gethostbyname(dir); /** convertimos la dirección dada // * en una dirección válida para el // * equipo y la metemos en addr // */ ////Comprobamos si ha ocurrido un error obtener el host //if(server == NULL){ // write(WRITE, "ERROR: Host inválido\n", DIM); // // terminamos la ejecución del programa // exit(-1); //} //// copiamos el contenido correspondiente a la dirección de server en addr //bcopy(server->h_addr, &(addr.sin_addr.s_addr), server->h_length); // //// realizamos la conexión al servidor //PDEBUG("INFO: Nos conectamos al servidor\n"); //error = connect(sock, (struct sockaddr*) &addr, length); //if(error < 0){ // char aux[] = "ERROR: al establecer la conexion con el servidor {{connect()}}: \n"; // write(WRITE, aux, strlen(aux)); // // terminamos la ejecución del programa // exit(-1); //} // //PDEBUG("INFO: Conexión establecida\n"); PDEBUG("INFO: Esperando mensaje de bienvenida\n"); memset(&auxMsj, 0, sizeof(sms)); if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){ PDEBUG("INFO: El socket está cerrado\n"); perror("Error al leer contenido del socket porque está cerrado\n"); perror("El cliente se parará\n"); exit(-1); } // atendemos la autentificación del cliente do{ bzero(aux, DIM); sprintf(aux, "%s ~$> %s\n", auxMsj.name, auxMsj.text); write(WRITE, aux, DIM); bzero(&auxMsj.text, SMS_LEN); if(auxMsj.flag == MSJ){ // si es un mensaje, solo imprimimos por la salida estandar memset(&auxMsj, 0, sizeof(sms)); if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){ PDEBUG("INFO: El socket está cerrado\n"); perror("Error al leer contenido del socket porque está cerrado\n"); perror("El cliente se parará\n"); exit(-1); } continue; } if(auxMsj.flag == REQ_PASS){ // si es password desactivamos el echo echo_off(); } nbytes = read(READ, &auxMsj.text, SMS_LEN); if(auxMsj.flag == REQ_PASS){// si es password activamos el echo echo_on(); } auxMsj.text[nbytes - 1] = '\0'; // eliminamos el retorno de carro // nos salimos? if(strcmp(auxMsj.text, "-x") == 0){ PDEBUG("EXIT: Cerrando el cliente, avisando al servidor...\n"); auxMsj.flag = CLI_EXIT; client_write(bio, &auxMsj, sizeof(sms)); PDEBUG("EXIT: Cerrando el socket\n"); shutdown(sock, 2); PDEBUG("EXIT: Cerrando el cliente\n"); exit(0); }else{ // es un mensaje strcpy(name, auxMsj.text); // hacemos una copia del nombre introducido para no perderlo if(auxMsj.flag == REQ_TEXT){ auxMsj.flag = REQ_AUTH; }else if(auxMsj.flag == REQ_PASS){ // entonces REQ_PASS auxMsj.flag = CHECK_PASS; }else if(auxMsj.flag == REQ_ROOM){ // entonces REQ_ROOM auxMsj.flag = CHECK_ROOM; } client_write(bio, &auxMsj, sizeof(sms)); memset(&auxMsj, 0, sizeof(sms)); if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){ PDEBUG("INFO: El socket está cerrado\n"); perror("Error al leer contenido del socket porque está cerrado\n"); perror("El cliente se parará\n"); exit(-1); } } }while(auxMsj.flag != OK); PDEBUG("INFO: Usuario conectado\n"); printf("Usuario conectado...\n"); fd_set desc, descCopy; // definimos un descriptor que contendrá nuestros descriptores //inicializamos la lista de conexiones FD_ZERO(&desc); // Inicio del bit descriptor sock con el valor de sock int fd; if(BIO_get_fd(bio, &fd) < 0){ write(WRITE, "ERROR: crear le descriptor %s\n", DIM); // terminamos la ejecución del programa exit(-1); } FD_SET (fd, &desc); // Inicio del bit descriptor connList con el valor del descriptor de entrada estándar FD_SET (READ, &desc); while(1){ // hacemos una copia de seguridad para asegurarnos de no perder los datos descCopy = desc; // ¿Hay algún socket listo para leer? PDEBUG("INFO: ¿Hay algún socket listo para leer?\n"); error = select(fd + 1, &descCopy, NULL, NULL, NULL); //Comprobamos si ha ocurrido un error al ponernos a escuchar if(error < 0){ write(WRITE, "ERROR: al realizar la selección {{select()}}: %s\n", DIM); // terminamos la ejecución del programa exit(-1); } // recorriendo los sockets para ver los que están activos PDEBUG("INFO: recorriendo los sockets para ver los que están activos\n"); if(FD_ISSET(fd, &descCopy)){ PDEBUG("INFO: Nuevo mensaje recibido\n"); if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){ PDEBUG("INFO: El socket está cerrado\n"); perror("Error al leer contenido del socket porque está cerrado\n"); perror("El cliente se parará\n"); exit(-1); } switch(auxMsj.flag){ case OK: // mensaje de aceptacion, mismo comportamiento que msj case MSJ: // mensaje recibido if(serverTalk != 1 || strcmp(auxMsj.name, SERVER) == 0){ PDEBUG("INFO: Recibido mensaje\n"); sprintf(aux, "%s ~$> %s\n", auxMsj.name, auxMsj.text); write(WRITE, aux, strlen(aux)); sync(); } break; case SERV_EXIT: // el servidor se va a cerrar PDEBUG("EXIT: El servidor se está cerrando, se dejará de leer\n"); shutdown(sock, SHUT_RDWR); sprintf(aux, "%s ~$> Servidor desconectado\n", SERVER); write(WRITE, aux, strlen(aux)); sprintf(aux, "El proceso cliente se cerrará\n"); write(WRITE, aux, strlen(aux)); exit(0); break; default: sprintf(aux, "Recibido un mensaje mal formado\n"); write(WRITE, aux, strlen(aux)); sync(); break; } }else if(FD_ISSET(READ, &descCopy)){ PDEBUG("INFO: Nuevo mensaje escrito\n"); bzero(&auxMsj.text, SMS_LEN); // inicializamos el array nbytes = read(READ, &auxMsj.text, SMS_LEN); // leemos de la entrada estándar auxMsj.text[nbytes - 1] = 0; // eliminamos el retorno de carro // nos salimos? if(strcmp(auxMsj.text, "-x") == 0 && serverTalk != 1){ PDEBUG("EXIT: Cerrando el cliente, avisando al servidor...\n"); auxMsj.flag = CLI_EXIT; }else if(strcmp(auxMsj.text, "--serv") == 0){ // queremos hablar con el servidor PDEBUG("SERV_ADMIN: Iniciando la comunicación directa con el servidor\n"); sprintf(aux, "%s ~$> Iniciada conversación con el servidor\n", SERVER); write(WRITE, aux, strlen(aux)); serverTalk = 1; continue; }else if(sscanf(auxMsj.text, "--mp %s", aux) == 1){ // queremos hablar con el servidor PDEBUG("MP: Mensaje privado detectado\n"); strcpy(auxMsj.to, aux); sprintf(aux, "%s ~$> Inserte el mensaje privado\n", SERVER); write(WRITE, aux, strlen(aux)); auxMsj.flag = MP; nbytes = read(READ, &auxMsj.text, SMS_LEN); // leemos de la entrada estándar auxMsj.text[nbytes - 1] = 0; // eliminamos el retorno de carro }else{ // es un mensaje if(serverTalk == 1){ PDEBUG("SERV_ADMIN: Enviando mensaje al servidor\n"); auxMsj.flag = SERV_ADMIN; if(strcmp(auxMsj.text, "exit") == 0){ serverTalk = 0; sprintf(aux, "%s ~$> Envio de mensajes de configuración terminada:\n", SERVER); write(WRITE, aux, strlen(aux)); continue; } }else{ auxMsj.flag = MSJ; } } strcpy(auxMsj.name, name); // hacemos una copia del nombre introducido para no perderlo PDEBUG("INFO: Enviando mensaje...\n"); client_write(bio, &auxMsj, sizeof(sms)); PDEBUG("INFO: Mensaje Enviado\n"); // nos salimos? if(auxMsj.flag == CLI_EXIT){ PDEBUG("EXIT: Cerrando el socket\n"); shutdown(sock, SHUT_RDWR); PDEBUG("EXIT: Cerrando el cliente\n"); exit(0); } } } return 0; }
//-------------------------------------------------------------------------------------------------- le_result_t secSocket_Connect ( secSocket_Ctx_t* ctxPtr, ///< [INOUT] Secure socket context pointer char* hostPtr, ///< [IN] Host to connect on uint16_t port, ///< [IN] Port to connect on SocketType_t type, ///< [IN] Socket type (TCP, UDP) int* fdPtr ///< [OUT] Socket file descriptor ) { SSL* sslPtr = NULL; BIO* bioPtr = NULL; char hostAndPort[HOST_ADDR_LEN + PORT_STR_LEN + 1]; le_result_t status = LE_FAULT; unsigned long code; if ((!ctxPtr) || (!hostPtr) || (!fdPtr)) { LE_ERROR("Invalid argument: ctxPtr %p, hostPtr %p fdPtr %p", ctxPtr, hostPtr, fdPtr); return LE_BAD_PARAMETER; } OpensslCtx_t* contextPtr = GetContext(ctxPtr); if (!contextPtr) { return LE_BAD_PARAMETER; } // Start the connection snprintf(hostAndPort, sizeof(hostAndPort), "%s:%d", hostPtr, port); LE_INFO("Connecting to %d/%s:%d - %s...", type, hostPtr, port, hostAndPort); // Clear the current thread's OpenSSL error queue ERR_clear_error(); // Setting up the BIO abstraction layer bioPtr = BIO_new_ssl_connect(contextPtr->sslCtxPtr); if (!bioPtr) { LE_ERROR("Unable to allocate and connect BIO"); goto err; } BIO_get_ssl(bioPtr, &sslPtr); if (!sslPtr) { LE_ERROR("Unable to locate SSL pointer"); goto err; } // Set the SSL_MODE_AUTO_RETRY flag: it will cause read/write operations to only return after // the handshake and successful completion SSL_set_mode(sslPtr, SSL_MODE_AUTO_RETRY); BIO_set_conn_hostname(bioPtr, hostAndPort); // Attempt to connect the supplied BIO and perform the handshake. // This function returns 1 if the connection was successfully established and 0 or -1 if the // connection failed. if (BIO_do_connect(bioPtr) != 1) { LE_ERROR("Unable to connect BIO to %s", hostAndPort); goto err; } // Get the FD linked to the BIO BIO_get_fd(bioPtr, fdPtr); BIO_socket_nbio(*fdPtr, 1); contextPtr->bioPtr = bioPtr; return LE_OK; err: code = ERR_peek_last_error(); if ((ERR_GET_LIB(code) == ERR_LIB_BIO) || (ERR_GET_LIB(code) == ERR_LIB_SSL)) { switch (ERR_GET_REASON(code)) { case ERR_R_MALLOC_FAILURE: status = LE_NO_MEMORY; break; case BIO_R_NULL_PARAMETER: status = LE_BAD_PARAMETER; break; #if defined(BIO_R_BAD_HOSTNAME_LOOKUP) case BIO_R_BAD_HOSTNAME_LOOKUP: status = LE_UNAVAILABLE; break; #endif case BIO_R_CONNECT_ERROR: status = LE_COMM_ERROR; break; #if defined(BIO_R_EOF_ON_MEMORY_BIO) case BIO_R_EOF_ON_MEMORY_BIO: status = LE_CLOSED; break; #endif default: status = LE_FAULT; break; } } return status; }
enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb) { SSL *ssl = NULL; int rslt; char const* origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_CONNECT)); if (!pb->options.useSSL) { return resolv_and_connect_wout_SSL(pb); } if (NULL == pb->pal.ctx) { PUBNUB_LOG_TRACE("pb=%p: Don't have SSL_CTX\n", pb); pb->pal.ctx = SSL_CTX_new(SSLv23_client_method()); if (NULL == pb->pal.ctx) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("pb=%p SSL_CTX_new failed\n", pb); return pbpal_resolv_resource_failure; } PUBNUB_LOG_TRACE("pb=%p: Got SSL_CTX\n", pb); add_pubnub_cert(pb->pal.ctx); } if (NULL == pb->pal.socket) { PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb); pb->pal.socket = BIO_new_ssl_connect(pb->pal.ctx); if (PUBNUB_TIMERS_API) { pb->pal.connect_timeout = time(NULL) + pb->transaction_timeout_ms/1000; } } else { BIO_get_ssl(pb->pal.socket, &ssl); if (NULL == ssl) { return resolv_and_connect_wout_SSL(pb); } ssl = NULL; } if (NULL == pb->pal.socket) { ERR_print_errors_cb(print_to_pubnub_log, NULL); return pbpal_resolv_resource_failure; } PUBNUB_LOG_TRACE("pb=%p: Using BIO == %p\n", pb, pb->pal.socket); BIO_get_ssl(pb->pal.socket, &ssl); PUBNUB_ASSERT(NULL != ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* maybe not auto_retry? */ if (pb->pal.session != NULL) { SSL_set_session(ssl, pb->pal.session); } BIO_set_conn_hostname(pb->pal.socket, origin); BIO_set_conn_port(pb->pal.socket, "https"); if (pb->pal.ip_timeout != 0) { if (pb->pal.ip_timeout < time(NULL)) { pb->pal.ip_timeout = 0; } else { PUBNUB_LOG_TRACE("SSL re-connect to: %d.%d.%d.%d\n", pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]); BIO_set_conn_ip(pb->pal.socket, pb->pal.ip); } } BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io); WATCH_ENUM(pb->options.use_blocking_io); if (BIO_do_connect(pb->pal.socket) <= 0) { if (BIO_should_retry(pb->pal.socket) && PUBNUB_TIMERS_API && (pb->pal.connect_timeout > time(NULL))) { PUBNUB_LOG_TRACE("pb=%p: BIO_should_retry\n", pb); return pbpal_connect_wouldblock; } /* Expire the IP for the next connect */ pb->pal.ip_timeout = 0; ERR_print_errors_cb(print_to_pubnub_log, NULL); if (pb->pal.session != NULL) { SSL_SESSION_free(pb->pal.session); pb->pal.session = NULL; } PUBNUB_LOG_ERROR("pb=%p: BIO_do_connect failed\n", pb); return pbpal_connect_failed; } PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb); { int fd = BIO_get_fd(pb->pal.socket, NULL); socket_set_rcv_timeout(fd, pb->transaction_timeout_ms); } rslt = SSL_get_verify_result(ssl); if (rslt != X509_V_OK) { PUBNUB_LOG_WARNING("pb=%p: SSL_get_verify_result() failed == %d(%s)\n", pb, rslt, X509_verify_cert_error_string(rslt)); ERR_print_errors_cb(print_to_pubnub_log, NULL); if (pb->options.fallbackSSL) { BIO_free_all(pb->pal.socket); pb->pal.socket = NULL; return resolv_and_connect_wout_SSL(pb); } return pbpal_connect_failed; } PUBNUB_LOG_INFO("pb=%p: SSL session reused: %s\n", pb, SSL_session_reused(ssl) ? "yes" : "no"); if (pb->pal.session != NULL) { SSL_SESSION_free(pb->pal.session); } pb->pal.session = SSL_get1_session(ssl); if (0 == pb->pal.ip_timeout) { pb->pal.ip_timeout = SSL_SESSION_get_time(pb->pal.session) + SSL_SESSION_get_timeout(pb->pal.session); memcpy(pb->pal.ip, BIO_get_conn_ip(pb->pal.socket), 4); } PUBNUB_LOG_TRACE("pb=%p: SSL connected to IP: %d.%d.%d.%d\n", pb, pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]); return pbpal_connect_success; }
int main(int argc, const char* argv[]) { if(argc != 4) { printf("Usage: %s <server> <port> <certificate>\n", argv[0]); exit(EXIT_FAILURE); } // Setting up buffer for receiving data char buf[MAXDATASIZE]; memset(buf, 0, MAXDATASIZE*sizeof(char)); // Init SSL SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); // Make connection struct addrinfo hints, *results, *i; int status; char ipstr[INET6_ADDRSTRLEN]; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; // Try to get addrinfo, exiting on error if ((status = getaddrinfo(argv[1], argv[2], &hints, &results)) != 0) { fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status)); exit(EXIT_FAILURE); } printf("IP addresses for %s:\n", argv[1]); for (i = results; i != NULL; i = i->ai_next) { void* addr; char* ipver; if (i->ai_family == AF_INET6) { struct sockaddr_in6* ipv6 = (struct sockaddr_in6*) i->ai_addr; addr = &(ipv6->sin6_addr); ipver = "IPv6"; } else { struct sockaddr_in* ipv4 = (struct sockaddr_in*) i->ai_addr; addr = &(ipv4->sin_addr); ipver = "IPv4"; } inet_ntop(i->ai_family, addr, ipstr, INET6_ADDRSTRLEN); printf(" %s: %s\n\n", ipver, ipstr); } // SSL BIO* b; char ipp[20]; memset(ipp, 0, 20*sizeof(char)); strcat(ipp, ipstr); strcat(ipp, ":"); strcat(ipp, argv[2]); printf("Connecting to %s\n", ipp); SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method()); SSL* ssl; b = BIO_new_ssl_connect(ctx); BIO_get_ssl(b, &ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); if(!b) printf("Error loading bio\n"); if(!SSL_CTX_load_verify_locations(ctx, argv[3], NULL)) { printf("Error loading trust store %s\n", argv[3]); exit(EXIT_FAILURE); } // Connect SSL BIO_set_conn_hostname(b, ipp); if(BIO_do_connect(b) <= 0) { printf("Error with making SSL connection\n"); exit(EXIT_FAILURE); } printf("Successful SSL connection\n"); // Check the certificate if(SSL_get_verify_result(ssl) != X509_V_OK) { printf("Certificate not valid\n"); exit(EXIT_FAILURE); } // Printing out the used cipher printf("SSL uses cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); // Checking the commonName matches the hostname X509* p; char p_CN[256]; p = SSL_get_peer_certificate(ssl); X509_NAME_get_text_by_NID(X509_get_subject_name(p), NID_commonName, p_CN, 256); printf("commonName: %s", p_CN); if(!strcmp(argv[1], p_CN)) printf("...OK\n"); else { printf("...FAILED\n"); exit(EXIT_FAILURE); } // Read validation code BIO_read(b, buf, 3); printf("Validation code: %s\n", buf); // Send validation:226143 strcat(buf, ":226143"); printf("Sending \"%s\"\n", buf); BIO_write(b, buf, 10); // Read response BIO_read(b, buf, MAXDATASIZE); printf("Received \"%s\"\n", buf); // Free structures and close BIO_free_all(b); SSL_CTX_free(ctx); return 0; }