static bool ConnSSL_Init_SSL(CONNECTION *c) { int ret; assert(c != NULL); #ifdef HAVE_LIBSSL if (!ssl_ctx) { Log(LOG_ERR, "Cannot init ssl_ctx: OpenSSL initialization failed at startup"); return false; } assert(c->ssl_state.ssl == NULL); c->ssl_state.ssl = SSL_new(ssl_ctx); if (!c->ssl_state.ssl) { LogOpenSSLError("SSL_new()", NULL); return false; } ret = SSL_set_fd(c->ssl_state.ssl, c->sock); if (ret != 1) { LogOpenSSLError("SSL_set_fd()", NULL); ConnSSL_Free(c); return false; } #endif #ifdef HAVE_LIBGNUTLS ret = gnutls_set_default_priority(c->ssl_state.gnutls_session); if (ret < 0) { Log(LOG_ERR, "gnutls_set_default_priority: %s", gnutls_strerror(ret)); ConnSSL_Free(c); return false; } /* * The intermediate (long) cast is here to avoid a warning like: * "cast to pointer from integer of different size" on 64-bit platforms. * There doesn't seem to be an alternate GNUTLS API we could use instead, see e.g. * http://www.mail-archive.com/[email protected]/msg00286.html */ gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock); ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred); if (ret < 0) { Log(LOG_ERR, "gnutls_credentials_set: %s", gnutls_strerror(ret)); ConnSSL_Free(c); return false; } gnutls_dh_set_prime_bits(c->ssl_state.gnutls_session, DH_BITS_MIN); #endif Conn_OPTION_ADD(c, CONN_SSL); return true; }
bool ConnSSL_InitLibrary( void ) { #ifdef HAVE_LIBSSL SSL_CTX *newctx; if (!ssl_ctx) { SSL_library_init(); SSL_load_error_strings(); } if (!RAND_status()) { Log(LOG_ERR, "OpenSSL PRNG not seeded: /dev/urandom missing?"); /* * it is probably best to fail and let the user install EGD or a similar program if no kernel random device is available. * According to OpenSSL RAND_egd(3): "The automatic query of /var/run/egd-pool et al was added in OpenSSL 0.9.7"; * so it makes little sense to deal with PRNGD seeding ourselves. */ return false; } newctx = SSL_CTX_new(SSLv23_method()); if (!newctx) { LogOpenSSLError("SSL_CTX_new()", NULL); return false; } if (!ConnSSL_LoadServerKey_openssl(newctx)) goto out; SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2); SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_free(ssl_ctx); ssl_ctx = newctx; Log(LOG_INFO, "%s initialized.", SSLeay_version(SSLEAY_VERSION)); return true; out: SSL_CTX_free(newctx); return false; #endif #ifdef HAVE_LIBGNUTLS int err; static bool initialized; if (initialized) /* TODO: cannot reload gnutls keys: can't simply free x509 context -- it may still be in use */ return false; err = gnutls_global_init(); if (err) { Log(LOG_ERR, "gnutls_global_init(): %s", gnutls_strerror(err)); return false; } if (!ConnSSL_LoadServerKey_gnutls()) return false; Log(LOG_INFO, "gnutls %s initialized.", gnutls_check_version(NULL)); initialized = true; return true; #endif }
static bool ConnSSL_LoadServerKey_openssl(SSL_CTX *ctx) { char *cert_key; assert(ctx); if (!Conf_SSLOptions.KeyFile) { Log(LOG_ERR, "No SSL server key configured!"); return false; } SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ctx, &Conf_SSLOptions.KeyFilePassword); if (SSL_CTX_use_PrivateKey_file(ctx, Conf_SSLOptions.KeyFile, SSL_FILETYPE_PEM) != 1) { array_free_wipe(&Conf_SSLOptions.KeyFilePassword); LogOpenSSLError("Failed to add private key", Conf_SSLOptions.KeyFile); return false; } cert_key = Conf_SSLOptions.CertFile ? Conf_SSLOptions.CertFile:Conf_SSLOptions.KeyFile; if (SSL_CTX_use_certificate_chain_file(ctx, cert_key) != 1) { array_free_wipe(&Conf_SSLOptions.KeyFilePassword); LogOpenSSLError("Failed to load certificate chain", cert_key); return false; } array_free_wipe(&Conf_SSLOptions.KeyFilePassword); if (!SSL_CTX_check_private_key(ctx)) { LogOpenSSLError("Server private key does not match certificate", NULL); return false; } if (Load_DH_params()) { if (SSL_CTX_set_tmp_dh(ctx, dh_params) != 1) LogOpenSSLError("Error setting DH parameters", Conf_SSLOptions.DHFile); /* don't return false here: the non-DH modes will still work */ DH_free(dh_params); dh_params = NULL; } return true; }
/** * Check and handle error return codes after failed calls to SSL functions. * * OpenSSL: * SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or * SSL_write() on ssl. * * GnuTLS: * gnutlsssl_read(), gnutls_write() or gnutls_handshake(). * * @param c The connection handle. * @prarm code The return code. * @param fname The name of the function in which the error occurred. * @return -1 on fatal errors, 0 if we can try again later. */ static int ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname) { #ifdef HAVE_LIBSSL int ret = SSL_ERROR_SYSCALL; unsigned long sslerr; int real_errno = errno; ret = SSL_get_error(c->ssl_state.ssl, code); switch (ret) { case SSL_ERROR_WANT_READ: io_event_del(c->sock, IO_WANTWRITE); Conn_OPTION_ADD(c, CONN_SSL_WANT_READ); return 0; /* try again later */ case SSL_ERROR_WANT_WRITE: io_event_del(c->sock, IO_WANTREAD); Conn_OPTION_ADD(c, CONN_SSL_WANT_WRITE); /* fall through */ case SSL_ERROR_NONE: return 0; /* try again later */ case SSL_ERROR_ZERO_RETURN: LogDebug("SSL connection shut down normally."); break; case SSL_ERROR_SYSCALL: /* SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, * and SSL_ERROR_WANT_X509_LOOKUP */ sslerr = ERR_get_error(); if (sslerr) { Log(LOG_ERR, "SSL error: %s [in %s()]!", ERR_error_string(sslerr, NULL), fname); } else { switch (code) { /* EOF that violated protocol */ case 0: Log(LOG_ERR, "SSL error, client disconnected [in %s()]!", fname); break; case -1: /* low level socket I/O error, check errno */ Log(LOG_ERR, "SSL error: %s [in %s()]!", strerror(real_errno), fname); } } break; case SSL_ERROR_SSL: LogOpenSSLError("SSL protocol error", fname); break; default: Log(LOG_ERR, "Unknown SSL error %d [in %s()]!", ret, fname); } ConnSSL_Free(c); return -1; #endif #ifdef HAVE_LIBGNUTLS switch (code) { case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: if (gnutls_record_get_direction(c->ssl_state.gnutls_session)) { Conn_OPTION_ADD(c, CONN_SSL_WANT_WRITE); io_event_del(c->sock, IO_WANTREAD); } else { Conn_OPTION_ADD(c, CONN_SSL_WANT_READ); io_event_del(c->sock, IO_WANTWRITE); } break; default: assert(code < 0); if (gnutls_error_is_fatal(code)) { Log(LOG_ERR, "SSL error: %s [%s].", gnutls_strerror(code), fname); ConnSSL_Free(c); return -1; } } return 0; #endif }
static bool ConnSSL_Init_SSL(CONNECTION *c) { int ret; LogDebug("Initializing SSL ..."); assert(c != NULL); #ifdef HAVE_LIBSSL if (!ssl_ctx) { Log(LOG_ERR, "Can't initialize SSL context, OpenSSL initialization failed at startup!"); return false; } assert(c->ssl_state.ssl == NULL); assert(c->ssl_state.fingerprint == NULL); c->ssl_state.ssl = SSL_new(ssl_ctx); if (!c->ssl_state.ssl) { LogOpenSSLError("Failed to create SSL structure", NULL); return false; } Conn_OPTION_ADD(c, CONN_SSL); ret = SSL_set_fd(c->ssl_state.ssl, c->sock); if (ret != 1) { LogOpenSSLError("Failed to set SSL file descriptor", NULL); ConnSSL_Free(c); return false; } #endif #ifdef HAVE_LIBGNUTLS Conn_OPTION_ADD(c, CONN_SSL); ret = gnutls_priority_set(c->ssl_state.gnutls_session, priorities_cache); if (ret != GNUTLS_E_SUCCESS) { Log(LOG_ERR, "Failed to set GnuTLS session priorities: %s", gnutls_strerror(ret)); ConnSSL_Free(c); return false; } /* * The intermediate (long) cast is here to avoid a warning like: * "cast to pointer from integer of different size" on 64-bit platforms. * There doesn't seem to be an alternate GNUTLS API we could use instead, see e.g. * http://www.mail-archive.com/[email protected]/msg00286.html */ gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock); gnutls_certificate_server_set_request(c->ssl_state.gnutls_session, GNUTLS_CERT_REQUEST); ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred); if (ret != 0) { Log(LOG_ERR, "Failed to set SSL credentials: %s", gnutls_strerror(ret)); ConnSSL_Free(c); return false; } gnutls_dh_set_prime_bits(c->ssl_state.gnutls_session, DH_BITS_MIN); #endif return true; }
bool ConnSSL_InitLibrary( void ) { if (!Conf_SSLInUse()) { LogDebug("SSL not in use, skipping initialization."); return true; } #ifdef HAVE_LIBSSL SSL_CTX *newctx; if (!ssl_ctx) { SSL_library_init(); SSL_load_error_strings(); } if (!RAND_status()) { Log(LOG_ERR, "OpenSSL PRNG not seeded: /dev/urandom missing?"); /* * it is probably best to fail and let the user install EGD or * a similar program if no kernel random device is available. * According to OpenSSL RAND_egd(3): "The automatic query of * /var/run/egd-pool et al was added in OpenSSL 0.9.7"; * so it makes little sense to deal with PRNGD seeding ourselves. */ array_free(&Conf_SSLOptions.ListenPorts); return false; } newctx = SSL_CTX_new(SSLv23_method()); if (!newctx) { LogOpenSSLError("Failed to create SSL context", NULL); array_free(&Conf_SSLOptions.ListenPorts); return false; } if (!ConnSSL_LoadServerKey_openssl(newctx)) goto out; if (SSL_CTX_set_cipher_list(newctx, Conf_SSLOptions.CipherList) == 0) { Log(LOG_ERR, "Failed to apply OpenSSL cipher list \"%s\"!", Conf_SSLOptions.CipherList); goto out; } SSL_CTX_set_session_id_context(newctx, (unsigned char *)"ngircd", 6); SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2); SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, Verify_openssl); SSL_CTX_free(ssl_ctx); ssl_ctx = newctx; Log(LOG_INFO, "%s initialized.", SSLeay_version(SSLEAY_VERSION)); return true; out: SSL_CTX_free(newctx); array_free(&Conf_SSLOptions.ListenPorts); return false; #endif #ifdef HAVE_LIBGNUTLS int err; static bool initialized; if (initialized) { /* TODO: cannot reload gnutls keys: can't simply free x509 * context -- it may still be in use */ return false; } err = gnutls_global_init(); if (err) { Log(LOG_ERR, "Failed to initialize GnuTLS: %s", gnutls_strerror(err)); goto out; } if (!ConnSSL_LoadServerKey_gnutls()) goto out; if (gnutls_priority_init(&priorities_cache, Conf_SSLOptions.CipherList, NULL) != GNUTLS_E_SUCCESS) { Log(LOG_ERR, "Failed to apply GnuTLS cipher list \"%s\"!", Conf_SSLOptions.CipherList); goto out; } Log(LOG_INFO, "GnuTLS %s initialized.", gnutls_check_version(NULL)); initialized = true; return true; out: array_free(&Conf_SSLOptions.ListenPorts); return false; #endif }