NOEXPORT int servername_cb(SSL *ssl, int *ad, void *arg) { SERVICE_OPTIONS *section=(SERVICE_OPTIONS *)arg; const char *servername=SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); SERVERNAME_LIST *list; CLI *c; #ifdef USE_LIBWRAP char *accepted_address; #endif /* USE_LIBWRAP */ /* leave the alert type at SSL_AD_UNRECOGNIZED_NAME */ (void)ad; /* squash the unused parameter warning */ if(!section->servername_list_head) { s_log(LOG_DEBUG, "SNI: no virtual services defined"); return SSL_TLSEXT_ERR_OK; } if(!servername) { s_log(LOG_NOTICE, "SNI: no servername received"); return SSL_TLSEXT_ERR_NOACK; } s_log(LOG_INFO, "SNI: requested servername: %s", servername); for(list=section->servername_list_head; list; list=list->next) if(matches_wildcard((char *)servername, list->servername)) { s_log(LOG_DEBUG, "SNI: matched pattern: %s", list->servername); c=SSL_get_ex_data(ssl, index_cli); c->opt=list->opt; SSL_set_SSL_CTX(ssl, c->opt->ctx); SSL_set_verify(ssl, SSL_CTX_get_verify_mode(c->opt->ctx), SSL_CTX_get_verify_callback(c->opt->ctx)); s_log(LOG_NOTICE, "SNI: switched to service [%s]", c->opt->servname); #ifdef USE_LIBWRAP accepted_address=s_ntop(&c->peer_addr, c->peer_addr_len); libwrap_auth(c, accepted_address); /* retry on a service switch */ str_free(accepted_address); #endif /* USE_LIBWRAP */ return SSL_TLSEXT_ERR_OK; } s_log(LOG_ERR, "SNI: no pattern matched servername: %s", servername); return SSL_TLSEXT_ERR_ALERT_FATAL; }
static void init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; addrlen=sizeof addr; if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { strcpy(c->accepted_address, "NOT A SOCKET"); c->local_rfd.is_socket=0; c->local_wfd.is_socket=0; /* TODO: It's not always true */ #ifdef USE_WIN32 if(get_last_socket_error()!=ENOTSOCK) { #else if(c->opt->option.transparent_src || get_last_socket_error()!=ENOTSOCK) { #endif sockerror("getpeerbyname"); longjmp(c->err, 1); } /* ignore ENOTSOCK error so 'local' doesn't have to be a socket */ } else { /* success */ /* copy addr to c->peer_addr */ memcpy(&c->peer_addr.addr[0], &addr, sizeof addr); c->peer_addr.num=1; s_ntop(c->accepted_address, &c->peer_addr.addr[0]); c->local_rfd.is_socket=1; c->local_wfd.is_socket=1; /* TODO: It's not always true */ /* it's a socket: lets setup options */ if(set_socket_options(c->local_rfd.fd, 1)<0) longjmp(c->err, 1); #ifdef USE_LIBWRAP libwrap_auth(c); #endif /* USE_LIBWRAP */ auth_user(c); s_log(LOG_NOTICE, "Service %s accepted connection from %s", c->opt->servname, c->accepted_address); } } static void init_remote(CLI *c) { /* create connection to host/service */ if(c->opt->source_addr.num) memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); #ifndef USE_WIN32 else if(c->opt->option.transparent_src) memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); #endif else { c->bind_addr.num=0; /* don't bind connecting socket */ } /* setup c->remote_fd, now */ if(c->opt->option.remote) c->remote_fd.fd=connect_remote(c); #ifdef SO_ORIGINAL_DST else if(c->opt->option.transparent_dst) c->remote_fd.fd=connect_transparent(c); #endif /* SO_ORIGINAL_DST */ else /* NOT in remote mode */ c->remote_fd.fd=connect_local(c); c->remote_fd.is_socket=1; /* always! */ s_log(LOG_DEBUG, "Remote FD=%d initialized", c->remote_fd.fd); if(set_socket_options(c->remote_fd.fd, 2)<0) longjmp(c->err, 1); } static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; if(!(c->ssl=SSL_new(c->opt->ctx))) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx, strlen(sid_ctx)); if(c->opt->option.client) { #ifndef OPENSSL_NO_TLSEXT if(c->opt->host_name) { s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->host_name); if(!SSL_set_tlsext_host_name(c->ssl, c->opt->host_name)) { sslerror("SSL_set_tlsext_host_name"); longjmp(c->err, 1); } } #endif if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* setup some values for transfer() function */ if(c->opt->option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { #if OPENSSL_VERSION_NUMBER<0x1000002f /* this critical section is a crude workaround for CVE-2010-3864 * * see http://www.securityfocus.com/bid/44884 for details * * NOTE: this critical section also covers callbacks (e.g. OCSP) */ enter_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ if(c->opt->option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); #if OPENSSL_VERSION_NUMBER<0x1000002f leave_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(&c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ if(c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
static void init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addr_len; char *accepted_address; /* check if local_rfd is a socket and get peer address */ addr_len=sizeof(SOCKADDR_UNION); c->local_rfd.is_socket=!getpeername(c->local_rfd.fd, &addr.sa, &addr_len); if(c->local_rfd.is_socket) { memcpy(&c->peer_addr.sa, &addr.sa, addr_len); c->peer_addr_len=addr_len; if(set_socket_options(c->local_rfd.fd, 1)) s_log(LOG_WARNING, "Failed to set local socket options"); } else { if(get_last_socket_error()!=S_ENOTSOCK) { sockerror("getpeerbyname (local_rfd)"); longjmp(c->err, 1); } } /* check if local_wfd is a socket and get peer address */ if(c->local_rfd.fd==c->local_wfd.fd) { c->local_wfd.is_socket=c->local_rfd.is_socket; } else { addr_len=sizeof(SOCKADDR_UNION); c->local_wfd.is_socket=!getpeername(c->local_wfd.fd, &addr.sa, &addr_len); if(c->local_wfd.is_socket) { if(!c->local_rfd.is_socket) { /* already retrieved */ memcpy(&c->peer_addr.sa, &addr.sa, addr_len); c->peer_addr_len=addr_len; } if(set_socket_options(c->local_wfd.fd, 1)) s_log(LOG_WARNING, "Failed to set local socket options"); } else { if(get_last_socket_error()!=S_ENOTSOCK) { sockerror("getpeerbyname (local_wfd)"); longjmp(c->err, 1); } } } /* neither of local descriptors is a socket */ if(!c->local_rfd.is_socket && !c->local_rfd.is_socket) { #ifndef USE_WIN32 if(c->opt->option.transparent_src) { s_log(LOG_ERR, "Transparent source needs a socket"); longjmp(c->err, 1); } #endif s_log(LOG_NOTICE, "Service [%s] accepted connection", c->opt->servname); return; } /* authenticate based on retrieved IP address of the client */ accepted_address=s_ntop(&c->peer_addr, c->peer_addr_len); #ifdef USE_LIBWRAP libwrap_auth(c, accepted_address); #endif /* USE_LIBWRAP */ auth_user(c, accepted_address); s_log(LOG_NOTICE, "Service [%s] accepted connection from %s", c->opt->servname, accepted_address); str_free(accepted_address); }