/** finish the ssl init. * Creates the SSL context + internal tls_extra_data and sets * extra_data to it. * Separated from tls_tcpconn_init to allow delayed ssl context * init (from the "child" process and not from the main one). * WARNING: the connection should be already locked. * @return 0 on success, -1 on errror. */ static int tls_complete_init(struct tcp_connection* c) { tls_domain_t* dom; struct tls_extra_data* data = 0; tls_domains_cfg_t* cfg; enum tls_conn_states state; str *sname = NULL; str *srvid = NULL; if (LOW_MEM_NEW_CONNECTION_TEST()){ ERR("tls: ssl bug #1491 workaround: not enough memory for safe" " operation: shm=%lu threshold1=%d\n", shm_available_safe(), cfg_get(tls, tls_cfg, low_mem_threshold1)); goto error2; } /* Get current TLS configuration and increase reference * count immediately. */ LM_DBG("completing tls connection initialization\n"); lock_get(tls_domains_cfg_lock); cfg = *tls_domains_cfg; /* Increment the reference count in the configuration structure, this * is to ensure that, while on the garbage queue, the configuration does * not get deleted if there are still connection referencing its SSL_CTX */ atomic_inc(&cfg->ref_count); lock_release(tls_domains_cfg_lock); if (c->flags & F_CONN_PASSIVE) { state=S_TLS_ACCEPTING; dom = tls_lookup_cfg(cfg, TLS_DOMAIN_SRV, &c->rcv.dst_ip, c->rcv.dst_port, 0, 0); } else { state=S_TLS_CONNECTING; sname = tls_get_connect_server_name(); srvid = tls_get_connect_server_id(); dom = tls_lookup_cfg(cfg, TLS_DOMAIN_CLI, &c->rcv.dst_ip, c->rcv.dst_port, sname, srvid); } if (unlikely(c->state<0)) { BUG("Invalid connection (state %d)\n", c->state); goto error; } DBG("Using initial TLS domain %s (dom %p ctx %p sn [%s])\n", tls_domain_str(dom), dom, dom->ctx[process_no], ZSW(dom->server_name.s)); data = (struct tls_extra_data*)shm_malloc(sizeof(struct tls_extra_data)); if (!data) { ERR("Not enough shared memory left\n"); goto error; } memset(data, '\0', sizeof(struct tls_extra_data)); data->ssl = SSL_new(dom->ctx[process_no]); data->rwbio = tls_BIO_new_mbuf(0, 0); data->cfg = cfg; data->state = state; if (unlikely(data->ssl == 0 || data->rwbio == 0)) { TLS_ERR("Failed to create SSL or BIO structure:"); if (data->ssl) SSL_free(data->ssl); if (data->rwbio) BIO_free(data->rwbio); goto error; } #ifndef OPENSSL_NO_TLSEXT if (sname!=NULL) { if(!SSL_set_tlsext_host_name(data->ssl, sname->s)) { if (data->ssl) SSL_free(data->ssl); if (data->rwbio) BIO_free(data->rwbio); goto error; } LM_DBG("outbound TLS server name set to: %s\n", sname->s); } #endif #if OPENSSL_VERSION_NUMBER < 0x010100000L #ifdef TLS_KSSL_WORKARROUND /* if needed apply workaround for openssl bug #1467 */ if (data->ssl->kssl_ctx && openssl_kssl_malloc_bug){ kssl_ctx_free(data->ssl->kssl_ctx); data->ssl->kssl_ctx=0; } #endif #endif SSL_set_bio(data->ssl, data->rwbio, data->rwbio); c->extra_data = data; /* link the extra data struct inside ssl connection*/ SSL_set_app_data(data->ssl, data); return 0; error: atomic_dec(&cfg->ref_count); if (data) shm_free(data); error2: return -1; }
/* * Called when new tcp connection is accepted or connected, create ssl * data structures here, there is no need to acquire any lock, because the * connection is being created by a new process and on other process has * access to it yet, this is called before adding the tcp_connection * structure into the hash */ int tls_tcpconn_init(struct tcp_connection *c, int sock) { struct tls_domain *dom; struct usr_avp *avp; int_str val; /* * new connection within a single process, no lock necessary */ LM_DBG("entered: Creating a whole new ssl connection\n"); /* * do everything tcpconn_new wouldn't do when TLS */ c->type = PROTO_TLS; c->rcv.proto = PROTO_TLS; c->flags = 0; c->timeout = get_ticks() + DEFAULT_TCP_CONNECTION_LIFETIME; if (c->state == S_CONN_ACCEPT) { LM_DBG("looking up socket based TLS server " "domain [%s:%d]\n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port); dom = tls_find_server_domain(&c->rcv.dst_ip, c->rcv.dst_port); if (dom) { LM_DBG("found socket based TLS server domain " "[%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); } else { LM_ERR("no TLS server domain found\n"); return -1; } } else if (c->state == S_CONN_CONNECT) { avp = NULL; if (tls_client_domain_avp > 0) { avp = search_first_avp(0, tls_client_domain_avp, &val, 0); } else { LM_DBG("name based TLS client domains are disabled\n"); } if (!avp) { LM_DBG("no TLS client doman AVP set, looking " "for socket based TLS client domain\n"); dom = tls_find_client_domain(&c->rcv.src_ip, c->rcv.src_port); if (dom) { LM_DBG("found socket based TLS client domain " "[%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); } else { LM_ERR("no TLS client domain found\n"); return -1; } } else { LM_DBG("TLS client domain AVP found = '%.*s'\n", val.s.len, ZSW(val.s.s)); dom = tls_find_client_domain_name(val.s); if (dom) { LM_DBG("found name based TLS client domain " "'%.*s'\n", val.s.len, ZSW(val.s.s)); c->extra_data = SSL_new(dom->ctx); } else { LM_DBG("no name based TLS client domain found, " "trying socket based TLS client domains\n"); dom = tls_find_client_domain(&c->rcv.src_ip, c->rcv.src_port); if (dom) { LM_DBG("found socket based TLS client domain [%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); } else { LM_ERR("no TLS client domain found\n"); return -1; } } } } else { LM_ERR("invalid connection state (bug in TCP code)\n"); return -1; } if (!c->extra_data) { LM_ERR("failed to create SSL structure\n"); return -1; } #ifndef OPENSSL_NO_KRB5 if ( ((SSL *)c->extra_data)->kssl_ctx ) { kssl_ctx_free( ((SSL *)c->extra_data)->kssl_ctx ); ((SSL *)c->extra_data)->kssl_ctx = 0; } #endif if (c->state == S_CONN_ACCEPT) { LM_DBG("Setting in ACCEPT mode (server)\n"); SSL_set_accept_state((SSL *) c->extra_data); } else if (c->state == S_CONN_CONNECT) { LM_DBG("Setting in CONNECT mode (client)\n"); SSL_set_connect_state((SSL *) c->extra_data); } return 0; }
/* * Wrapper around SSL_accept, returns -1 on error, 0 on success */ static int tls_accept(struct tcp_connection *c, short *poll_events) { int ret, err; SSL *ssl; X509* cert; if ( (c->proto_flags&F_TLS_DO_ACCEPT)==0 ) { LM_BUG("invalid connection state (bug in TLS code)\n"); return -1; } ssl = (SSL *) c->extra_data; #ifndef OPENSSL_NO_KRB5 if ( ssl->kssl_ctx==NULL ) ssl->kssl_ctx = kssl_ctx_new( ); #endif ret = SSL_accept(ssl); #ifndef OPENSSL_NO_KRB5 if ( ssl->kssl_ctx ) { kssl_ctx_free( ssl->kssl_ctx ); ssl->kssl_ctx = 0; } #endif if (ret > 0) { LM_INFO("New TLS connection from %s:%d accepted\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); /* TLS accept done, reset the flag */ c->proto_flags &= ~F_TLS_DO_ACCEPT; LM_DBG("new TLS connection from %s:%d using %s %s %d\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl), SSL_get_cipher_bits(ssl, 0) ); LM_DBG("local socket: %s:%d\n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port ); cert = SSL_get_peer_certificate(ssl); if (cert != 0) { tls_dump_cert_info("tls_accept: client TLS certificate", cert); if (SSL_get_verify_result(ssl) != X509_V_OK) { LM_WARN("TLS client certificate verification failed\n"); tls_dump_verification_failure(SSL_get_verify_result(ssl)); } X509_free(cert); } else { LM_INFO("Client did not present a TLS certificate\n"); } cert = SSL_get_certificate(ssl); if (cert != 0) { tls_dump_cert_info("tls_accept: local TLS server certificate", cert); } else { /* this should not happen, servers always present a cert */ LM_ERR("local TLS server domain has no certificate\n"); } return 0; } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: LM_INFO("TLS connection from %s:%d accept failed cleanly\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); c->state = S_CONN_BAD; return -1; case SSL_ERROR_WANT_READ: if (poll_events) *poll_events = POLLIN; return 0; case SSL_ERROR_WANT_WRITE: if (poll_events) *poll_events = POLLOUT; return 0; default: c->state = S_CONN_BAD; if (errno == 0) { LM_ERR("New TLS connection from %s:%d failed to accept:" " rejected by client\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); } else { LM_ERR("New TLS connection from %s:%d failed to accept\n", ip_addr2a(&c->rcv.src_ip), c->rcv.src_port); LM_ERR("TLS error: (ret=%d, err=%d, errno=%d/%s):\n", ret, err, errno, strerror(errno)); tls_print_errstack(); } return -1; } } LM_BUG("bug\n"); return -1; }
static int tls_conn_init(struct tcp_connection* c) { struct tls_domain *dom; struct usr_avp *avp; int_str val; /* * new connection within a single process, no lock necessary */ LM_DBG("entered: Creating a whole new ssl connection\n"); if ( c->flags&F_CONN_ACCEPTED ) { /* connection created as a result of an accept -> server */ c->proto_flags = F_TLS_DO_ACCEPT; LM_DBG("looking up socket based TLS server " "domain [%s:%d]\n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port); dom = tls_find_server_domain(&c->rcv.dst_ip, c->rcv.dst_port); if (dom) { LM_DBG("found socket based TLS server domain " "[%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); } else { LM_ERR("no TLS server domain found\n"); return -1; } } else { /* connection created as a result of a connect -> client */ avp = NULL; c->proto_flags = F_TLS_DO_CONNECT; if (tls_client_domain_avp > 0) { avp = search_first_avp(0, tls_client_domain_avp, &val, 0); } else { LM_DBG("name based TLS client domains are disabled\n"); } if (!avp) { LM_DBG("no TLS client doman AVP set, looking " "for socket based TLS client domain\n"); dom = tls_find_client_domain(&c->rcv.src_ip, c->rcv.src_port); if (dom) { LM_DBG("found socket based TLS client domain " "[%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); } else { LM_ERR("no TLS client domain found\n"); return -1; } } else { LM_DBG("TLS client domain AVP found = '%.*s'\n", val.s.len, ZSW(val.s.s)); dom = tls_find_client_domain_name(val.s); if (dom) { LM_DBG("found name based TLS client domain " "'%.*s'\n", val.s.len, ZSW(val.s.s)); c->extra_data = SSL_new(dom->ctx); } else { LM_DBG("no name based TLS client domain found, " "trying socket based TLS client domains\n"); dom = tls_find_client_domain(&c->rcv.src_ip, c->rcv.src_port); if (dom) { LM_DBG("found socket based TLS client domain [%s:%d]\n", ip_addr2a(&dom->addr), dom->port); c->extra_data = SSL_new(dom->ctx); } else { LM_ERR("no TLS client domain found\n"); return -1; } } } } if (!c->extra_data) { LM_ERR("failed to create SSL structure\n"); return -1; } #ifndef OPENSSL_NO_KRB5 if ( ((SSL *)c->extra_data)->kssl_ctx ) { kssl_ctx_free( ((SSL *)c->extra_data)->kssl_ctx ); ((SSL *)c->extra_data)->kssl_ctx = 0; } #endif if ( c->proto_flags & F_TLS_DO_ACCEPT ) { LM_DBG("Setting in ACCEPT mode (server)\n"); SSL_set_accept_state((SSL *) c->extra_data); } else { LM_DBG("Setting in CONNECT mode (client)\n"); SSL_set_connect_state((SSL *) c->extra_data); } return 0; }