Example #1
0
wi_uinteger_t wi_socket_cipher_bits(wi_socket_t *socket) {
#ifdef HAVE_OPENSSL_SSL_H
	return SSL_get_cipher_bits(socket->ssl, NULL);
#else
	return 0;
#endif
}
Example #2
0
uint32_t wi_socket_cipher_bits(wi_socket_t *socket) {
#ifdef WI_SSL
	return SSL_get_cipher_bits(socket->ssl, NULL);
#else
	return 0;
#endif
}
Example #3
0
const char*
io_strio(struct io *io)
{
	static char	buf[128];
	char		ssl[128];

	ssl[0] = '\0';
#ifdef IO_SSL
	if (io->ssl) {
		(void)snprintf(ssl, sizeof ssl, " ssl=%s:%s:%d",
		    SSL_get_version(io->ssl),
		    SSL_get_cipher_name(io->ssl),
		    SSL_get_cipher_bits(io->ssl, NULL));
	}
#endif

	if (io->iobuf == NULL)
		(void)snprintf(buf, sizeof buf,
		    "<io:%p fd=%d to=%d fl=%s%s>",
		    io, io->sock, io->timeout, io_strflags(io->flags), ssl);
	else
		(void)snprintf(buf, sizeof buf,
		    "<io:%p fd=%d to=%d fl=%s%s ib=%zu ob=%zu>",
		    io, io->sock, io->timeout, io_strflags(io->flags), ssl,
		    io_pending(io), io_queued(io));

	return (buf);
}
Example #4
0
File: command.c Project: 50wu/gpdb
/*
 * printSSLInfo
 *
 * Prints information about the current SSL connection, if SSL is in use
 */
static void
printSSLInfo(void)
{
#ifdef USE_SSL
	int			sslbits = -1;
	SSL		   *ssl;

	ssl = PQgetssl(pset.db);
	if (!ssl)
		return;					/* no SSL */

	SSL_get_cipher_bits(ssl, &sslbits);
	printf(_("SSL connection (cipher: %s, bits: %i)\n"),
		   SSL_get_cipher(ssl), sslbits);
#else

	/*
	 * If psql is compiled without SSL but is using a libpq with SSL, we
	 * cannot figure out the specifics about the connection. But we know it's
	 * SSL secured.
	 */
	if (PQgetssl(pset.db))
		printf(_("SSL connection (unknown cipher)\n"));
#endif
}
Example #5
0
File: ssl.c Project: ezc/elinks
unsigned char *
get_ssl_connection_cipher(struct socket *socket)
{
	ssl_t *ssl = socket->ssl;
	struct string str;

	if (!init_string(&str)) return NULL;

#ifdef USE_OPENSSL
	add_format_to_string(&str, "%ld-bit %s %s",
		SSL_get_cipher_bits(ssl, NULL),
		SSL_get_cipher_version(ssl),
		SSL_get_cipher_name(ssl));
#elif defined(CONFIG_GNUTLS)
	/* XXX: How to get other relevant parameters? */
	add_format_to_string(&str, "%s - %s - %s - %s - %s (compr: %s)",
		gnutls_protocol_get_name(gnutls_protocol_get_version(*ssl)),
		gnutls_kx_get_name(gnutls_kx_get(*ssl)),
		gnutls_cipher_get_name(gnutls_cipher_get(*ssl)),
		gnutls_mac_get_name(gnutls_mac_get(*ssl)),
		gnutls_certificate_type_get_name(gnutls_certificate_type_get(*ssl)),
		gnutls_compression_get_name(gnutls_compression_get(*ssl)));
#endif

	return str.source;
}
Example #6
0
static void set_cipher_info(TLS_REC *tls, SSL *ssl)
{
	g_return_if_fail(tls != NULL);
	g_return_if_fail(ssl != NULL);

	tls_rec_set_protocol_version(tls, SSL_get_version(ssl));

	tls_rec_set_cipher(tls, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
	tls_rec_set_cipher_size(tls, SSL_get_cipher_bits(ssl, NULL));
}
Example #7
0
const char *
ssl_to_text(const SSL *ssl)
{
	static char buf[256];

	(void)snprintf(buf, sizeof buf, "version=%s, cipher=%s, bits=%d",
	    SSL_get_cipher_version(ssl),
	    SSL_get_cipher_name(ssl),
	    SSL_get_cipher_bits(ssl, NULL));

	return (buf);
}
Example #8
0
/** wrapper around SSL_connect, using SSL return convention.
 * It will also log critical errors and certificate debugging info.
 * @param c - tcp connection with tls (extra_data must be a filled
 *            tcp_extra_data structure). The state must be S_TLS_CONNECTING.
 * @param error  set to the error reason (SSL_ERROR_*).
 *            Note that it can be SSL_ERROR_NONE while the return is < 0
 *            ("internal" error, not at the SSL level, see below).
 * @return >=1 on success, 0 and <0 on error. 0 means the underlying SSL
 *           connection was closed/shutdown. < 0 is returned for any
 *           SSL_ERROR (including  WANT_READ or WANT_WRITE), but also
 *           for internal non SSL related errors (in this case -2 is
 *           returned and error==SSL_ERROR_NONE).
 *
 */
int tls_connect(struct tcp_connection *c, int* error)
{
	SSL *ssl;
	int ret;
	X509* cert;
	struct tls_extra_data* tls_c;
	int tls_log;

	*error = SSL_ERROR_NONE;
	tls_c=(struct tls_extra_data*)c->extra_data;
	ssl=tls_c->ssl;
	
	if (unlikely(tls_c->state != S_TLS_CONNECTING)) {
		BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state);
		goto err;
	}
	ret = SSL_connect(ssl);
	if (unlikely(ret == 1)) {
		DBG("TLS connect successful\n");
		tls_c->state = S_TLS_ESTABLISHED;
		tls_log = cfg_get(tls, tls_cfg, log);
		LOG(tls_log, "tls_connect: new connection to %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)
		    );
		LOG(tls_log, "tls_connect: sending 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_connect: server certificate", cert);
			if (SSL_get_verify_result(ssl) != X509_V_OK) {
				LOG(tls_log, "WARNING: tls_connect: server certificate "
				    "verification failed!!!\n");
				tls_dump_verification_failure(SSL_get_verify_result(ssl));
			}
			X509_free(cert);
		} else {
			/* this should not happen, servers always present a cert */
			LOG(tls_log, "tls_connect: server did not "
							"present a certificate\n");
		}
		tls_run_event_routes(c);
	} else { /* 0 or < 0 */
		*error = SSL_get_error(ssl, ret);
	}
	return ret;
err:
	/* internal non openssl related errors */
	return -2;
}
Example #9
0
int
be_tls_get_cipher_bits(Port *port)
{
	int			bits;

	if (port->ssl)
	{
		SSL_get_cipher_bits(port->ssl, &bits);
		return bits;
	}
	else
		return 0;
}
Example #10
0
static int
check_cipher_strength(SSL *ssl, ImapUserCb user_cb, void *user_arg)
{
  int ok, bits = SSL_get_cipher_bits(ssl, NULL);

  if (bits > 40) 
    return 1;
  ok = 0;
  if (user_cb != NULL)
    user_cb(IME_TLS_WEAK_CIPHER, user_arg,
                    bits, &ok);
  return ok;
}
Example #11
0
   CipherInfo
   TCPConnection::GetCipherInfo()
   {
      if (!is_ssl_)
      {
         throw std::logic_error("Session is not SSL/TLS. Cipher info cannot be retrieved.");
      }

      auto ssl_handle = ssl_socket_.native_handle();
      AnsiString name = SSL_get_cipher_name(ssl_handle);
      AnsiString version = SSL_get_version(ssl_handle);
      int bits = SSL_get_cipher_bits(ssl_handle, 0);
      return CipherInfo(name, version, bits);
   }
Example #12
0
/** wrapper around SSL_accept, usin SSL return convention.
 * It will also log critical errors and certificate debugging info.
 * @param c - tcp connection with tls (extra_data must be a filled
 *            tcp_extra_data structure). The state must be S_TLS_ACCEPTING.
 * @param error  set to the error reason (SSL_ERROR_*).
 *            Note that it can be SSL_ERROR_NONE while the return is < 0
 *            ("internal" error, not at the SSL level, see below).
 * @return >=1 on success, 0 and <0 on error. 0 means the underlying SSL
 *           connection was closed/shutdown.  < 0 is returned for any
 *           SSL_ERROR (including  WANT_READ or WANT_WRITE), but also
 *           for internal non SSL related errors (in this case -2 is
 *           returned and error==SSL_ERROR_NONE).
 *
 */
int tls_accept(struct tcp_connection *c, int* error)
{
	int ret;
	SSL *ssl;
	X509* cert;
	struct tls_extra_data* tls_c;
	int tls_log;

	*error = SSL_ERROR_NONE;
	tls_c=(struct tls_extra_data*)c->extra_data;
	ssl=tls_c->ssl;
	
	if (unlikely(tls_c->state != S_TLS_ACCEPTING)) {
		BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state);
		goto err;
	}
	ret = SSL_accept(ssl);
	if (unlikely(ret == 1)) {
		DBG("TLS accept successful\n");
		tls_c->state = S_TLS_ESTABLISHED;
		tls_log = cfg_get(tls, tls_cfg, log);
		LOG(tls_log, "tls_accept: new 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)
		    );
		LOG(tls_log, "tls_accept: 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 certificate", cert);
			if (SSL_get_verify_result(ssl) != X509_V_OK) {
				LOG(tls_log, "WARNING: tls_accept: client certificate "
				    "verification failed!!!\n");
				tls_dump_verification_failure(SSL_get_verify_result(ssl));
			}
			X509_free(cert);
		} else {
			LOG(tls_log, "tls_accept: client did not present a certificate\n");
		}
	} else { /* ret == 0 or < 0 */
		*error = SSL_get_error(ssl, ret);
	}
	return ret;
err:
	/* internal non openssl related errors */
	return -2;
}
Example #13
0
int verify_ssl_cipher(SSL *ssl)
{
	unsigned char *cipher;
#ifdef HAVE_SSLV2_CLIENT_METHOD
	if (SSL_get_ssl_method(ssl) == SSLv2_client_method())
		return S_INSECURE_CIPHER;
#endif
#ifdef HAVE_SSLV3_CLIENT_METHOD
	if (SSL_get_ssl_method(ssl) == SSLv3_client_method())
		return S_INSECURE_CIPHER;
#endif
	if (SSL_get_cipher_bits(ssl, NULL) < 112)
		return S_INSECURE_CIPHER;
	cipher = cast_uchar SSL_get_cipher_name(ssl);
	if (cipher && strstr(cast_const_char cipher, "RC4-"))
		return S_INSECURE_CIPHER;
	return 0;
}
Example #14
0
const char *
ssl_to_text(const SSL *ssl)
{
	static char	buf[256];
	static char	description[128];
	char	       *tls_version = NULL;

	/*
	 * SSL_get_cipher_version() does not know about the exact TLS version...
	 * you have to pick it up from second field of the SSL cipher description !
	 */
	SSL_CIPHER_description(SSL_get_current_cipher(ssl), description, sizeof description);
	tls_version = strchr(description, ' ') + 1;
	tls_version[strcspn(tls_version, " ")] = '\0';
	(void)snprintf(buf, sizeof buf, "version=%s (%s), cipher=%s, bits=%d",
	    SSL_get_cipher_version(ssl),
	    tls_version,
	    SSL_get_cipher_name(ssl),
	    SSL_get_cipher_bits(ssl, NULL));

	return (buf);
}
Example #15
0
/* side effect: fills the ssl->fingerprint buffer */
static gboolean
ssl_verify_certificate (LmSSL *ssl, const gchar *server)
{
    gboolean retval = TRUE;
    gboolean match_result = FALSE;
    LmSSLBase *base;
    long verify_res;
    int rc;
    const EVP_MD *digest = EVP_sha256();
    unsigned int digest_len;
    guchar digest_bin[EVP_MD_size(digest)];
    X509 *srv_crt;
    gchar *cn;
    X509_NAME *crt_subj;

    base = LM_SSL_BASE(ssl);

    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s: Cipher: %s/%s/%i\n",
           __FILE__,
           SSL_get_cipher_version(ssl->ssl),
           SSL_get_cipher_name(ssl->ssl),
           SSL_get_cipher_bits(ssl->ssl, NULL));

    verify_res = SSL_get_verify_result(ssl->ssl);
    srv_crt = SSL_get_peer_certificate(ssl->ssl);
    rc = X509_digest(srv_crt, digest, digest_bin, &digest_len);
    if ((rc != 0) && (digest_len == EVP_MD_size(digest))) {
        _lm_ssl_base_set_fingerprint(base, digest_bin, digest_len);
        if (_lm_ssl_base_check_fingerprint(base) != 0) {
            if (base->func(ssl,
                           LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
                           base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }
    } else {
      if (base->func(ssl,
                     LM_SSL_STATUS_GENERIC_ERROR,
                     base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
          return FALSE;
      }
    }
    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s: SSL_get_verify_result() = %ld\n",
           __FILE__,
           verify_res);
    switch (verify_res) {
    case X509_V_OK:
        break;
    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
    case X509_V_ERR_UNABLE_TO_GET_CRL:
        if (base->func(ssl,
                       LM_SSL_STATUS_NO_CERT_FOUND,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
        /* special case for self signed certificates? */
    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
    case X509_V_ERR_INVALID_CA:
    case X509_V_ERR_CERT_UNTRUSTED:
    case X509_V_ERR_CERT_REVOKED:
        if (base->func(ssl,
                       LM_SSL_STATUS_UNTRUSTED_CERT,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_CERT_NOT_YET_VALID:
    case X509_V_ERR_CRL_NOT_YET_VALID:
        if (base->func(ssl,
                       LM_SSL_STATUS_CERT_NOT_ACTIVATED,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_CERT_HAS_EXPIRED:
    case X509_V_ERR_CRL_HAS_EXPIRED:
        if (base->func(ssl,
                       LM_SSL_STATUS_CERT_EXPIRED,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    default:
        if (base->func(ssl, LM_SSL_STATUS_GENERIC_ERROR,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
    }
    /*if (retval == FALSE) {
      g_set_error (error, LM_ERROR, LM_ERROR_CONNECTION_OPEN,
      ssl_get_x509_err(verify_res), NULL);
      }*/
    crt_subj = X509_get_subject_name(srv_crt);
    cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1);

    /* FWB: deprecated call, can only get first entry */
    if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) {

        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
               "%s: server = '%s', cn = '%s'\n",
               __FILE__, server, cn);

        if (cn != NULL && ssl_match_domain_name(server, cn)) {
            match_result = TRUE;
        } else {
            /* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: CN does not match server name\n", __FILE__); */
        }
    } else {
        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
               "X509_NAME_get_text_by_NID() failed");
    }

    /* RFC6125: "...However, it is perfectly acceptable for the subject field to be empty,
     * as long as the certificate contains a subject alternative name ("subjectAltName")
     * extension that includes at least one subjectAltName entry"
     */
    if (!match_result) {
        /* FWB: CN doesn't match, try SANs */
        int subject_alt_names_nb = -1;
        int san_counter;
        STACK_OF(GENERAL_NAME) *subject_alt_names = NULL;

        // Try to extract the names within the SAN extension from the certificate
        subject_alt_names = X509_get_ext_d2i((X509 *) srv_crt, NID_subject_alt_name, NULL, NULL);
        if (subject_alt_names != NULL) {

            // Check each name within the extension
            subject_alt_names_nb = sk_GENERAL_NAME_num(subject_alt_names);
            for (san_counter=0; san_counter<subject_alt_names_nb; san_counter++) {
                const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(subject_alt_names, san_counter);
                if (current_name->type == GEN_DNS) {
                    // Current name is a DNS name, let's check it, it's ASCII
                    if (ssl_match_domain_name(server, (const char *)current_name->d.dNSName->data)) {
                        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s' - MATCH\n", __FILE__, current_name->d.dNSName->data);
                        match_result = TRUE; /* break; */
                    } else {
                        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s'\n", __FILE__, current_name->d.dNSName->data);
                    }
                }
            }

        }

        sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free);
    }

    if (!match_result) {
        if (base->func (ssl,
                        LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
    }

    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n",
           __FILE__,
           X509_NAME_oneline(X509_get_issuer_name(srv_crt), NULL, 0),
           X509_NAME_oneline(X509_get_subject_name(srv_crt), NULL, 0),
           cn);

    g_free(cn);

    return retval;
}
Example #16
0
int h2o_socket_get_ssl_cipher_bits(h2o_socket_t *sock)
{
    return sock->ssl != NULL ? SSL_get_cipher_bits(sock->ssl->ssl, NULL) : 0;
}
Example #17
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;
}
Example #18
0
static gboolean
ssl_verify_certificate (LmSSL *ssl, const gchar *server)
{
    gboolean retval = TRUE;
    LmSSLBase *base;
    long verify_res;
    unsigned int digest_len;
    X509 *srv_crt;
    gchar *cn;
    X509_NAME *crt_subj;

    base = LM_SSL_BASE(ssl);

    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s: Cipher: %s/%s/%i\n",
           __FILE__,
           SSL_get_cipher_version(ssl->ssl),
           SSL_get_cipher_name(ssl->ssl),
           SSL_get_cipher_bits(ssl->ssl, NULL));

    verify_res = SSL_get_verify_result(ssl->ssl);
    srv_crt = SSL_get_peer_certificate(ssl->ssl);
    if (base->expected_fingerprint != NULL) {
        X509_digest(srv_crt, EVP_md5(), (guchar *) base->fingerprint,
                    &digest_len);
        if (memcmp(base->expected_fingerprint, base->fingerprint,
                   digest_len) != 0) {
            if (base->func(ssl,
                           LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
                           base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }
    }
    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s: SSL_get_verify_result() = %ld\n",
           __FILE__,
           verify_res);
    switch (verify_res) {
    case X509_V_OK:
        break;
    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
        /* special case for self signed certificates? */
    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
    case X509_V_ERR_UNABLE_TO_GET_CRL:
    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
        if (base->func(ssl,
                       LM_SSL_STATUS_NO_CERT_FOUND,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_INVALID_CA:
    case X509_V_ERR_CERT_UNTRUSTED:
    case X509_V_ERR_CERT_REVOKED:
        if (base->func(ssl,
                       LM_SSL_STATUS_UNTRUSTED_CERT,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_CERT_NOT_YET_VALID:
    case X509_V_ERR_CRL_NOT_YET_VALID:
        if (base->func(ssl,
                       LM_SSL_STATUS_CERT_NOT_ACTIVATED,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_CERT_HAS_EXPIRED:
    case X509_V_ERR_CRL_HAS_EXPIRED:
        if (base->func(ssl,
                       LM_SSL_STATUS_CERT_EXPIRED,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    default:
        if (base->func(ssl, LM_SSL_STATUS_GENERIC_ERROR,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
    }
    /*if (retval == FALSE) {
      g_set_error (error, LM_ERROR, LM_ERROR_CONNECTION_OPEN,
      ssl_get_x509_err(verify_res), NULL);
      }*/
    crt_subj = X509_get_subject_name(srv_crt);
    cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1);
    
    if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) {
        gchar *domain = cn;

        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
               "%s: server = '%s', cn = '%s'\n",
               __FILE__, server, cn);
        
        if ((cn[0] == '*') && (cn[1] == '.')) {
            domain = strstr (cn, server);
        }

        if ((domain == NULL) || (strncmp (server, domain, LM_SSL_CN_MAX) != 0)) {
            if (base->func (ssl,
                            LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
                            base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                retval = FALSE;
            }
        }
    } else {
        g_warning ("X509_NAME_get_text_by_NID() failed");
    }

    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, 
           "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n",
           __FILE__,
           X509_NAME_oneline(X509_get_issuer_name(srv_crt), NULL, 0),
           X509_NAME_oneline(X509_get_subject_name(srv_crt), NULL, 0),
           cn);

    g_free(cn);
    
    return retval;
}
Example #19
0
void wr_connect(char *host, int port, char *login, char *password) {
	SHA_CTX					c;
	static unsigned char	hex[] = "0123456789abcdef";
	unsigned char			sha[SHA_DIGEST_LENGTH];
	struct hostent			*hp;
	int						i, on = 1;

	/* disconnect active socket */
	if(wr_socket >= 0)
		wr_close();

	/* reset current working directory */
	strlcpy(wr_files_cwd, "/", sizeof(wr_files_cwd));

	/* copy values */
	wr_port = port;
	
	if(port != 2000)
		snprintf(wr_host, sizeof(wr_host), "%s:%d", host, port);
	else
		strlcpy(wr_host, host, sizeof(wr_host));

	strlcpy(wr_login, login, sizeof(wr_login));
	strlcpy(wr_password, password, sizeof(wr_password));

	/* log */
	wr_printf_prefix("Connecting to %s...\n", wr_host);

	/* create new socket */
	wr_socket = socket(AF_INET, SOCK_STREAM, 0);

	if(wr_socket < 0) {
		wr_printf_prefix("Could not create a socket: %s\n",
			strerror(errno));
		wr_close();

		return;
	}

	/* set socket options */
	if(setsockopt(wr_socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
		wr_printf_prefix("Could not set socket options: %s\n",
			strerror(errno));
		wr_close();

		return;
	}

	/* init address */
	memset(&wr_addr, 0, sizeof(wr_addr));
	wr_addr.sin_family	= AF_INET;
	wr_addr.sin_port	= htons(port);

	if(!inet_aton(host, &wr_addr.sin_addr)) {
		hp = gethostbyname(host);

		if(!hp) {
			wr_printf_prefix("Could not resolve hostname %s: %s\n",
				host, hstrerror(h_errno));
			wr_close();

			return;
		}

		memcpy(&wr_addr.sin_addr, hp->h_addr, sizeof(wr_addr.sin_addr));
	}

	/* connect TCP socket */
	if(connect(wr_socket, (struct sockaddr *) &wr_addr, sizeof(wr_addr)) < 0) {
		wr_printf_prefix("Could not connect to %s: %s\n",
			host, strerror(errno));
		wr_close();

		return;
	}

	/* create SSL context */
	wr_ssl_ctx = SSL_CTX_new(TLSv1_client_method());

	if(!wr_ssl_ctx) {
		wr_printf_prefix("Could not create SSL context: %s\n",
			ERR_reason_error_string(ERR_get_error()));
		wr_close();

		return;
	}

	if(SSL_CTX_set_cipher_list(wr_ssl_ctx, "ALL") != 1) {
		wr_printf_prefix("Could not set SSL cipher list: %s\n",
			ERR_reason_error_string(ERR_get_error()));
		wr_close();

		return;
	}

	/* create SSL socket */
	wr_ssl = SSL_new(wr_ssl_ctx);

	if(!wr_ssl) {
		wr_printf_prefix("Could not create SSL socket: %s\n",
			ERR_reason_error_string(ERR_get_error()));
		wr_close();

		return;
	}

	if(SSL_set_fd(wr_ssl, wr_socket) != 1) {
		wr_printf_prefix("Could not set SSL file descriptor: %s\n",
			ERR_reason_error_string(ERR_get_error()));
		wr_close();

		return;
	}

	if(SSL_connect(wr_ssl) != 1) {
		wr_printf_prefix("Could not connect to %s via SSL: %s\n",
			host, ERR_reason_error_string(ERR_get_error()));
		wr_close();

		return;
	}

	/* log */
	wr_printf_prefix("Connected using %s/%s/%u bits, logging in...\n",
		SSL_get_cipher_version(wr_ssl),
		SSL_get_cipher_name(wr_ssl),
		SSL_get_cipher_bits(wr_ssl, NULL));

	/* send initial login */
	wr_send_command("HELLO%s", WR_MESSAGE_SEPARATOR);
	
	/* hash the password */
	memset(wr_password_sha, 0, sizeof(wr_password_sha));
	
	if(strlen(wr_password) > 0) {
		SHA1_Init(&c);
		SHA1_Update(&c, (unsigned char *) wr_password, strlen(wr_password));
		SHA1_Final(sha, &c);
	
		/* map into hexademical characters */
		for(i = 0; i < SHA_DIGEST_LENGTH; i++) {
			wr_password_sha[i+i]	= hex[sha[i] >> 4];
			wr_password_sha[i+i+1]	= hex[sha[i] & 0x0F];
		}
			
		wr_password_sha[i+i] = '\0';
	}
Example #20
0
bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
{
    const char *mechlist = NULL;
    sasl_security_properties_t secprops;
    int err;
    char *localAddr, *remoteAddr;
    int mechlistlen;
    RedsSASL *sasl = &stream->priv->sasl;

    if (!(localAddr = reds_stream_get_local_address(stream))) {
        goto error;
    }

    if (!(remoteAddr = reds_stream_get_remote_address(stream))) {
        free(localAddr);
        goto error;
    }

    err = sasl_server_new("spice",
                          NULL, /* FQDN - just delegates to gethostname */
                          NULL, /* User realm */
                          localAddr,
                          remoteAddr,
                          NULL, /* Callbacks, not needed */
                          SASL_SUCCESS_DATA,
                          &sasl->conn);
    free(localAddr);
    free(remoteAddr);
    localAddr = remoteAddr = NULL;

    if (err != SASL_OK) {
        spice_warning("sasl context setup failed %d (%s)",
                    err, sasl_errstring(err, NULL, NULL));
        sasl->conn = NULL;
        goto error;
    }

    /* Inform SASL that we've got an external SSF layer from TLS */
    if (stream->priv->ssl) {
        sasl_ssf_t ssf;

        ssf = SSL_get_cipher_bits(stream->priv->ssl, NULL);
        err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
        if (err != SASL_OK) {
            spice_warning("cannot set SASL external SSF %d (%s)",
                        err, sasl_errstring(err, NULL, NULL));
            goto error_dispose;
        }
    } else {
        sasl->wantSSF = 1;
    }

    memset(&secprops, 0, sizeof secprops);
    /* Inform SASL that we've got an external SSF layer from TLS */
    if (stream->priv->ssl) {
        /* If we've got TLS (or UNIX domain sock), we don't care about SSF */
        secprops.min_ssf = 0;
        secprops.max_ssf = 0;
        secprops.maxbufsize = 8192;
        secprops.security_flags = 0;
    } else {
        /* Plain TCP, better get an SSF layer */
        secprops.min_ssf = 56; /* Good enough to require kerberos */
        secprops.max_ssf = 100000; /* Arbitrary big number */
        secprops.maxbufsize = 8192;
        /* Forbid any anonymous or trivially crackable auth */
        secprops.security_flags =
            SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
    }

    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
    if (err != SASL_OK) {
        spice_warning("cannot set SASL security props %d (%s)",
                      err, sasl_errstring(err, NULL, NULL));
        goto error_dispose;
    }

    err = sasl_listmech(sasl->conn,
                        NULL, /* Don't need to set user */
                        "", /* Prefix */
                        ",", /* Separator */
                        "", /* Suffix */
                        &mechlist,
                        NULL,
                        NULL);
    if (err != SASL_OK || mechlist == NULL) {
        spice_warning("cannot list SASL mechanisms %d (%s)",
                      err, sasl_errdetail(sasl->conn));
        goto error_dispose;
    }

    spice_debug("Available mechanisms for client: '%s'", mechlist);

    sasl->mechlist = spice_strdup(mechlist);

    mechlistlen = strlen(mechlist);
    if (!reds_stream_write_all(stream, &mechlistlen, sizeof(uint32_t))
        || !reds_stream_write_all(stream, sasl->mechlist, mechlistlen)) {
        spice_warning("SASL mechanisms write error");
        goto error;
    }

    spice_debug("Wait for client mechname length");
    reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
                           read_cb, opaque);

    return true;

error_dispose:
    sasl_dispose(&sasl->conn);
    sasl->conn = NULL;
error:
    return false;
}
Example #21
0
/*
 * wrapper around SSL_connect, returns 0 on success, -1 on error
 */
static int tls_connect(struct tcp_connection *c, short *poll_events)
{
	int ret, err;
	SSL *ssl;
	X509* cert;

	if ( (c->proto_flags&F_TLS_DO_CONNECT)==0 ) {
		LM_BUG("invalid connection state (bug in TLS code)\n");
		return -1;
	}

	ssl = (SSL *) c->extra_data;

	ret = SSL_connect(ssl);
	if (ret > 0) {
		LM_INFO("New TLS connection to %s:%d established\n",
			ip_addr2a(&c->rcv.src_ip), c->rcv.src_port);
		c->proto_flags &= ~F_TLS_DO_CONNECT;
		LM_DBG("new TLS connection to %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("sending 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_connect: server TLS certificate", cert);
			if (SSL_get_verify_result(ssl) != X509_V_OK) {
				LM_WARN("TLS server certificate verification failed\n");
				tls_dump_verification_failure(SSL_get_verify_result(ssl));
			}
			X509_free(cert);
		} else {
			/* this should not happen, servers always present a cert */
			LM_ERR("server did not present a TLS certificate\n");
		}
		cert = SSL_get_certificate(ssl);
		if (cert != 0) {
			tls_dump_cert_info("tls_connect: local TLS client certificate",
				cert);
		} else {
			LM_INFO("local TLS client domain does not have a certificate\n");
		}
		return 0;
	} else {
		err = SSL_get_error(ssl, ret);
		switch (err) {
			case SSL_ERROR_ZERO_RETURN:
				LM_INFO("New TLS connection to %s:%d 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;
			case SSL_ERROR_SYSCALL:
				LM_ERR("SSL_ERROR_SYSCALL err=%s(%d)\n",
					strerror(errno), errno);
			default:
				LM_ERR("New TLS connection to %s:%d failed\n",
					ip_addr2a(&c->rcv.src_ip), c->rcv.src_port);
				LM_ERR("TLS error: %d (ret=%d) err=%s(%d)\n",
					err,ret,strerror(errno), errno);
				c->state = S_CONN_BAD;
				tls_print_errstack();
				return -1;
		}
	}

	LM_BUG("bug\n");
	return -1;
}
Example #22
0
void
session_pickup(struct session *s, struct submit_status *ss)
{
	void	*ssl;

	s->s_flags &= ~F_WAITIMSG;

	if ((ss != NULL && ss->code == 421) ||
	    (s->s_dstatus & DS_TEMPFAILURE)) {
		stat_increment("smtp.tempfail", 1);
		session_respond(s, "421 Service temporarily unavailable");
		session_enter_state(s, S_QUIT);
		io_reload(&s->s_io);
		return;
	}

	switch (s->s_state) {

	case S_CONNECTED:
		session_enter_state(s, S_INIT);
		s->s_msg.session_id = s->s_id;
		s->s_msg.ss = s->s_ss;
		session_imsg(s, PROC_MFA, IMSG_MFA_CONNECT, 0, 0, -1,
			     &s->s_msg, sizeof(s->s_msg));
		break;

	case S_INIT:
		if (ss->code != 250) {
			session_destroy(s, "rejected by filter");
			return;
		}

		if (s->s_l->flags & F_SMTPS) {
			ssl = ssl_smtp_init(s->s_l->ssl_ctx);
			io_set_read(&s->s_io);
			io_start_tls(&s->s_io, ssl);
			return;
		}

		session_respond(s, SMTPD_BANNER, env->sc_hostname);
		session_enter_state(s, S_GREETED);
		break;

	case S_AUTH_FINALIZE:
		if (s->s_flags & F_AUTHENTICATED)
			session_respond(s, "235 Authentication succeeded");
		else
			session_respond(s, "535 Authentication failed");
		session_enter_state(s, S_HELO);
		break;

	case S_RSET:
		session_respond(s, "250 2.0.0 Reset state");
		session_enter_state(s, S_HELO);
		break;

	case S_HELO:
		if (ss->code != 250) {
			session_enter_state(s, S_GREETED);
			session_respond(s, "%d Helo rejected", ss->code);
			break;
		}

		session_respond(s, "250%c%s Hello %s [%s], pleased to meet you",
		    (s->s_flags & F_EHLO) ? '-' : ' ',
		    env->sc_hostname, s->s_msg.helo, ss_to_text(&s->s_ss));

		if (s->s_flags & F_EHLO) {
			/* unconditionnal extensions go first */
			session_respond(s, "250-8BITMIME");
			session_respond(s, "250-ENHANCEDSTATUSCODES");

			/* XXX - we also want to support reading SIZE from MAIL parameters */
			session_respond(s, "250-SIZE %zu", env->sc_maxsize);

			if (ADVERTISE_TLS(s))
				session_respond(s, "250-STARTTLS");

			if (ADVERTISE_AUTH(s))
				session_respond(s, "250-AUTH PLAIN LOGIN");
			session_respond(s, "250 HELP");
		}
		break;

	case S_MAIL_MFA:
		if (ss->code != 250) {
			session_enter_state(s, S_HELO);
			session_respond(s, "%d Sender rejected", ss->code);
			break;
		}

		session_enter_state(s, S_MAIL_QUEUE);
		s->s_msg.sender = ss->u.maddr;

		session_imsg(s, PROC_QUEUE, IMSG_QUEUE_CREATE_MESSAGE, 0, 0, -1,
		    &s->s_msg, sizeof(s->s_msg));
		break;

	case S_MAIL_QUEUE:
		session_enter_state(s, S_MAIL);
		session_respond(s, "%d 2.1.0 Sender ok", ss->code);
		break;

	case S_RCPT_MFA:
		/* recipient was not accepted */
		if (ss->code != 250) {
			/* We do not have a valid recipient, downgrade state */
			if (s->rcptcount == 0)
				session_enter_state(s, S_MAIL);
			else
				session_enter_state(s, S_RCPT);
			session_respond(s, "%d 5.0.0 Recipient rejected: %s@%s", ss->code,
			    s->s_msg.rcpt.user,
			    s->s_msg.rcpt.domain);
			break;
		}

		session_enter_state(s, S_RCPT);
		s->rcptcount++;
		s->s_msg.dest = ss->u.maddr;
		session_respond(s, "%d 2.0.0 Recipient ok", ss->code);
		break;

	case S_DATA_QUEUE:
		session_enter_state(s, S_DATACONTENT);
		session_respond(s, "354 Enter mail, end with \".\" on a line by"
		    " itself");

		fprintf(s->datafp, "Received: from %s (%s [%s])\n",
		    s->s_msg.helo, s->s_hostname, ss_to_text(&s->s_ss));
		fprintf(s->datafp, "\tby %s (OpenSMTPD) with %sSMTP id %08x",
		    env->sc_hostname, s->s_flags & F_EHLO ? "E" : "",
		    evpid_to_msgid(s->s_msg.id));

		if (s->s_flags & F_SECURE) {
			fprintf(s->datafp, "\n\t(version=%s cipher=%s bits=%d)",
			    SSL_get_cipher_version(s->s_io.ssl),
			    SSL_get_cipher_name(s->s_io.ssl),
			    SSL_get_cipher_bits(s->s_io.ssl, NULL));
		}
		if (s->rcptcount == 1)
			fprintf(s->datafp, "\n\tfor <%s@%s>; ",
			    s->s_msg.rcpt.user,
			    s->s_msg.rcpt.domain);
		else
			fprintf(s->datafp, ";\n\t");

		fprintf(s->datafp, "%s\n", time_to_text(time(NULL)));
		break;

	case S_DATACONTENT:
		if (ss->code != 250)
			s->s_dstatus |= DS_PERMFAILURE;
		session_read_data(s, ss->u.dataline);
		break;

	case S_DONE:
		session_respond(s, "250 2.0.0 %08x Message accepted for delivery",
		    evpid_to_msgid(s->s_msg.id));
		log_info("%08x: from=<%s%s%s>, size=%ld, nrcpts=%zu, proto=%s, "
		    "relay=%s [%s]",
		    evpid_to_msgid(s->s_msg.id),
		    s->s_msg.sender.user,
		    s->s_msg.sender.user[0] == '\0' ? "" : "@",
		    s->s_msg.sender.domain,
		    s->s_datalen,
		    s->rcptcount,
		    s->s_flags & F_EHLO ? "ESMTP" : "SMTP",
		    s->s_hostname,
		    ss_to_text(&s->s_ss));

		session_enter_state(s, S_HELO);
		s->s_msg.id = 0;
		bzero(&s->s_nresp, sizeof(s->s_nresp));
		break;

	default:
		fatal("session_pickup: unknown state");
	}

	io_reload(&s->s_io);
}
Example #23
0
void http_got_header(struct connection *c, struct read_buffer *rb)
{
    int cf;
    int state = c->state != S_PROC ? S_GETH : S_PROC;
    unsigned char *head;
    unsigned char *cookie, *ch;
    int a, h, version;
    unsigned char *d;
    struct cache_entry *e;
    struct http_connection_info *info;
    unsigned char *host = upcase(c->url[0]) != 'P' ? c->url : get_url_data(c->url);
    set_timeout(c);
    info = c->info;
    if (rb->close == 2) {
        unsigned char *h;
        if (!c->tries && (h = get_host_name(host))) {
            if (info->bl_flags & BL_NO_CHARSET) {
                del_blacklist_entry(h, BL_NO_CHARSET);
            } else {
                add_blacklist_entry(h, BL_NO_CHARSET);
                c->tries = -1;
            }
            mem_free(h);
        }
        setcstate(c, S_CANT_READ);
        retry_connection(c);
        return;
    }
    rb->close = 0;
again:
    if ((a = get_header(rb)) == -1) {
        setcstate(c, S_HTTP_ERROR);
        abort_connection(c);
        return;
    }
    if (!a) {
        read_from_socket(c, c->sock1, rb, http_got_header);
        setcstate(c, state);
        return;
    }
    if (a != -2) {
        head = mem_alloc(a + 1);
        memcpy(head, rb->data, a);
        head[a] = 0;
        kill_buffer_data(rb, a);
    } else {
        head = stracpy("HTTP/0.9 200 OK\r\nContent-Type: text/html\r\n\r\n");
    }
    if (get_http_code(head, &h, &version) || h == 101) {
        mem_free(head);
        setcstate(c, S_HTTP_ERROR);
        abort_connection(c);
        return;
    }
    if (check_http_server_bugs(host, c->info, head) && is_connection_restartable(c)) {
        mem_free(head);
        setcstate(c, S_RESTART);
        retry_connection(c);
        return;
    }
    ch = head;
    while ((cookie = parse_http_header(ch, "Set-Cookie", &ch))) {
        unsigned char *host = upcase(c->url[0]) != 'P' ? c->url : get_url_data(c->url);
        set_cookie(NULL, host, cookie);
        mem_free(cookie);
    }
    if (h == 100) {
        mem_free(head);
        state = S_PROC;
        goto again;
    }
    if (h < 200) {
        mem_free(head);
        setcstate(c, S_HTTP_ERROR);
        abort_connection(c);
        return;
    }
    if (h == 204) {
        mem_free(head);
        setcstate(c, S_HTTP_204);
        http_end_request(c, 0);
        return;
    }
    if (h == 304) {
        mem_free(head);
        setcstate(c, S_OK);
        http_end_request(c, 1);
        return;
    }
    if ((h == 500 || h == 502 || h == 503 || h == 504) && http_bugs.retry_internal_errors && is_connection_restartable(c)) {
        /* !!! FIXME: wait some time ... */
        mem_free(head);
        setcstate(c, S_RESTART);
        retry_connection(c);
        return;
    }
    if (!c->cache && get_cache_entry(c->url, &c->cache)) {
        mem_free(head);
        setcstate(c, S_OUT_OF_MEM);
        abort_connection(c);
        return;
    }
    e = c->cache;
    e->http_code = h;
    if (e->head) mem_free(e->head);
    e->head = head;
    if ((d = parse_http_header(head, "Expires", NULL))) {
        time_t t = parse_http_date(d);
        if (t && e->expire_time != 1) e->expire_time = t;
        mem_free(d);
    }
    if ((d = parse_http_header(head, "Pragma", NULL))) {
        if (!casecmp(d, "no-cache", 8)) e->expire_time = 1;
        mem_free(d);
    }
    if ((d = parse_http_header(head, "Cache-Control", NULL))) {
        char *f = d;
        while (1) {
            while (*f && (*f == ' ' || *f == ',')) f++;
            if (!*f) break;
            if (!casecmp(f, "no-cache", 8) || !casecmp(f, "must-revalidate", 15)) {
                e->expire_time = 1;
            }
            if (!casecmp(f, "max-age=", 8)) {
                if (e->expire_time != 1) e->expire_time = time(NULL) + atoi(f + 8);
            }
            while (*f && *f != ',') f++;
        }
        mem_free(d);
    }
#ifdef HAVE_SSL
    if (c->ssl) {
        int l = 0;
        if (e->ssl_info) mem_free(e->ssl_info);
        e->ssl_info = init_str();
        add_num_to_str(&e->ssl_info, &l, SSL_get_cipher_bits(c->ssl, NULL));
        add_to_str(&e->ssl_info, &l, "-bit ");
        add_to_str(&e->ssl_info, &l, SSL_get_cipher_version(c->ssl));
        add_to_str(&e->ssl_info, &l, " ");
        add_to_str(&e->ssl_info, &l, (unsigned  char *)SSL_get_cipher_name(c->ssl));
    }
#endif
    if (e->redirect) mem_free(e->redirect), e->redirect = NULL;
    if (h == 301 || h == 302 || h == 303 || h == 307) {
        if ((h == 302 || h == 307) && !e->expire_time) e->expire_time = 1;
        if ((d = parse_http_header(e->head, "Location", NULL))) {
            unsigned char *user, *ins;
            unsigned char *newuser, *newpassword;
            if (!parse_url(d, NULL, &user, NULL, NULL, NULL, &ins, NULL, NULL, NULL, NULL, NULL, NULL) && !user && ins && (newuser = get_user_name(host))) {
                if (*newuser) {
                    int ins_off = ins - d;
                    newpassword = get_pass(host);
                    if (!newpassword) newpassword = stracpy("");
                    add_to_strn(&newuser, ":");
                    add_to_strn(&newuser, newpassword);
                    add_to_strn(&newuser, "@");
                    extend_str(&d, strlen(newuser));
                    ins = d + ins_off;
                    memmove(ins + strlen(newuser), ins, strlen(ins) + 1);
                    memcpy(ins, newuser, strlen(newuser));
                    mem_free(newpassword);
                }
                mem_free(newuser);
            }
            if (e->redirect) mem_free(e->redirect);
            e->redirect = d;
            e->redirect_get = h == 303;
        }
    }
    if (!e->expire_time && strchr(c->url, POST_CHAR)) e->expire_time = 1;
    info->close = 0;
    info->length = -1;
    info->version = version;
    if ((d = parse_http_header(e->head, "Connection", NULL)) || (d = parse_http_header(e->head, "Proxy-Connection", NULL))) {
        if (!strcasecmp(d, "close")) info->close = 1;
        mem_free(d);
    } else if (version < 11) info->close = 1;
    cf = c->from;
    c->from = 0;
    if ((d = parse_http_header(e->head, "Content-Range", NULL))) {
        if (strlen(d) > 6) {
            d[5] = 0;
            if (!(strcasecmp(d, "bytes")) && d[6] >= '0' && d[6] <= '9') {
#if defined(HAVE_STRTOLL)
                long long f = strtoll(d + 6, NULL, 10);
#elif defined(HAVE_STRTOQ)
                longlong f = strtoq(d + 6, NULL, 10);
#else
                long f = strtol(d + 6, NULL, 10);
                if (f == MAXLONG) f = -1;
#endif
                if (f >= 0 && (off_t)f >= 0 && (off_t)f == f) c->from = f;
            }
        }
        mem_free(d);
    }
    if (cf && !c->from && !c->unrestartable) c->unrestartable = 1;
    if (c->from > cf || c->from < 0) {
        setcstate(c, S_HTTP_ERROR);
        abort_connection(c);
        return;
    }
    if ((d = parse_http_header(e->head, "Content-Length", NULL))) {
        unsigned char *ep;
#if defined(HAVE_STRTOLL)
        long long l = strtoll(d, (char **)(void *)&ep, 10);
#elif defined(HAVE_STRTOQ)
        longlong l = strtoq(d, (char **)(void *)&ep, 10);
#else
        long l = strtol(d, (char **)(void *)&ep, 10);
        if (l == MAXLONG) l = -1;
#endif
        if (!*ep && l >= 0 && (off_t)l >= 0 && (off_t)l == l) {
            if (!info->close || version >= 11) info->length = l;
            if (c->from + l >= 0) c->est_length = c->from + l;
        }
        mem_free(d);
    }
    if ((d = parse_http_header(e->head, "Accept-Ranges", NULL))) {
        if (!strcasecmp(d, "none") && !c->unrestartable) c->unrestartable = 1;
        mem_free(d);
    } else {
        if (!c->unrestartable && !c->from) c->unrestartable = 1;
    }
    if (info->bl_flags & BL_NO_RANGE && !c->unrestartable) c->unrestartable = 1;
    if ((d = parse_http_header(e->head, "Transfer-Encoding", NULL))) {
        if (!strcasecmp(d, "chunked")) {
            info->length = -2;
            info->chunk_remaining = -1;
        }
        mem_free(d);
    }
    if (!info->close && info->length == -1) info->close = 1;
    if ((d = parse_http_header(e->head, "Last-Modified", NULL))) {
        if (e->last_modified && strcasecmp(e->last_modified, d)) {
            delete_entry_content(e);
            if (c->from) {
                c->from = 0;
                mem_free(d);
                setcstate(c, S_MODIFIED);
                retry_connection(c);
                return;
            }
        }
        if (!e->last_modified) e->last_modified = d;
        else mem_free(d);
    }
    if (!e->last_modified && (d = parse_http_header(e->head, "Date", NULL)))
        e->last_modified = d;
    if (info->length == -1 || (version < 11 && info->close)) rb->close = 1;
    read_http_data(c, rb);
}