From: Andrey Volk Date: Mon, 25 May 2026 21:25:56 +0000 (+0300) Subject: [mod_commands, mod_verto] Add new reloadcert API and let mod_verto reload certificate... X-Git-Tag: v1.11.1^2~13 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=b5c3c86aa078b0e63cdee4361f4b73f5991de0b9;p=thirdparty%2Ffreeswitch.git [mod_commands, mod_verto] Add new reloadcert API and let mod_verto reload certificates on the fly without disconnects. (#3033) --- diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 294c64ee3f..a68d5bbf70 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -2075,6 +2075,7 @@ typedef uint32_t switch_io_flag_t; SWITCH_EVENT_CALL_DETAIL SWITCH_EVENT_DEVICE_STATE SWITCH_EVENT_SHUTDOWN_REQUESTED - Shutdown of the system has been requested + SWITCH_EVENT_CERT_RELOAD - SSL/TLS certificates reload has been requested SWITCH_EVENT_ALL - All events at once @@ -2172,6 +2173,7 @@ typedef enum { SWITCH_EVENT_DEVICE_STATE, SWITCH_EVENT_TEXT, SWITCH_EVENT_SHUTDOWN_REQUESTED, + SWITCH_EVENT_CERT_RELOAD, SWITCH_EVENT_ALL } switch_event_types_t; diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 22e25ea8a6..b44035044a 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -2860,6 +2860,22 @@ SWITCH_STANDARD_API(reload_xml_function) return SWITCH_STATUS_SUCCESS; } +SWITCH_STANDARD_API(reload_cert_function) +{ + switch_event_t *event; + + if (switch_event_create(&event, SWITCH_EVENT_CERT_RELOAD) == SWITCH_STATUS_SUCCESS) { + switch_event_fire(&event); + stream->write_function(stream, "+OK cert reload event sent\n"); + + return SWITCH_STATUS_SUCCESS; + } + + stream->write_function(stream, "-ERR failed to create event\n"); + + return SWITCH_STATUS_FALSE; +} + #define KILL_SYNTAX " [cause]" SWITCH_STANDARD_API(kill_function) { @@ -7656,6 +7672,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "reloadacl", "Reload ACL", reload_acl_function, ""); SWITCH_ADD_API(commands_api_interface, "reload", "Reload module", reload_function, UNLOAD_SYNTAX); SWITCH_ADD_API(commands_api_interface, "reloadxml", "Reload XML", reload_xml_function, ""); + SWITCH_ADD_API(commands_api_interface, "reloadcert", "Reload SSL/TLS certificates", reload_cert_function, ""); SWITCH_ADD_API(commands_api_interface, "replace", "Replace a string", replace_function, "||"); SWITCH_ADD_API(commands_api_interface, "say_string", "", say_string_function, SAY_STRING_SYNTAX); SWITCH_ADD_API(commands_api_interface, "sched_api", "Schedule an api command", sched_api_function, SCHED_SYNTAX); @@ -7831,6 +7848,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) switch_console_set_complete("add nat_map status"); switch_console_set_complete("add reload ::console::list_loaded_modules"); switch_console_set_complete("add reloadacl reloadxml"); + switch_console_set_complete("add reloadcert"); switch_console_set_complete("add show aliases"); switch_console_set_complete("add show api"); switch_console_set_complete("add show application"); diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c index 1363416139..113a21c579 100644 --- a/src/mod/endpoints/mod_verto/mod_verto.c +++ b/src/mod/endpoints/mod_verto/mod_verto.c @@ -150,108 +150,162 @@ static void verto_deinit_ssl(verto_profile_t *profile) } } -static void close_file(ks_socket_t *sock) +static SSL_CTX *verto_create_ssl_ctx(verto_profile_t *profile, const char **errp) { - if (*sock != KS_SOCK_INVALID) { -#ifndef WIN32 - close(*sock); -#else - closesocket(*sock); -#endif - *sock = KS_SOCK_INVALID; - } -} + SSL_CTX *ctx = SSL_CTX_new(profile->ssl_method); -static void close_socket(ks_socket_t *sock) -{ - if (*sock != KS_SOCK_INVALID) { - shutdown(*sock, 2); - close_file(sock); - } -} - -void verto_broadcast(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id, void *user_data); + if (!ctx) { + *errp = "Failed to create SSL context"; -static int verto_init_ssl(verto_profile_t *profile) -{ - const char *err; - int i = 0; - - profile->ssl_method = SSLv23_server_method(); /* create server instance */ - profile->ssl_ctx = SSL_CTX_new(profile->ssl_method); /* create context */ - profile->ssl_ready = 1; - assert(profile->ssl_ctx); + return NULL; + } /* Disable SSLv2 */ - SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); /* Disable SSLv3 */ - SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); /* Disable TLSv1 */ - SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_TLSv1); + SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1); /* Disable Compression CRIME (Compression Ratio Info-leak Made Easy) */ - SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_COMPRESSION); + SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); - /* set the local certificate from CertFile */ if (!zstr(profile->chain)) { if (switch_file_exists(profile->chain, NULL) != SWITCH_STATUS_SUCCESS) { - err = "SUPPLIED CHAIN FILE NOT FOUND\n"; + *errp = "SUPPLIED CHAIN FILE NOT FOUND"; goto fail; } - if (!SSL_CTX_use_certificate_chain_file(profile->ssl_ctx, profile->chain)) { - err = "CERT CHAIN FILE ERROR"; + if (!SSL_CTX_use_certificate_chain_file(ctx, profile->chain)) { + *errp = "CERT CHAIN FILE ERROR"; goto fail; } } if (switch_file_exists(profile->cert, NULL) != SWITCH_STATUS_SUCCESS) { - err = "SUPPLIED CERT FILE NOT FOUND\n"; + *errp = "SUPPLIED CERT FILE NOT FOUND"; goto fail; } - if (!SSL_CTX_use_certificate_file(profile->ssl_ctx, profile->cert, SSL_FILETYPE_PEM)) { - err = "CERT FILE ERROR"; + if (!SSL_CTX_use_certificate_file(ctx, profile->cert, SSL_FILETYPE_PEM)) { + *errp = "CERT FILE ERROR"; goto fail; } - /* set the private key from KeyFile */ - if (switch_file_exists(profile->key, NULL) != SWITCH_STATUS_SUCCESS) { - err = "SUPPLIED KEY FILE NOT FOUND\n"; + *errp = "SUPPLIED KEY FILE NOT FOUND"; goto fail; } - if (!SSL_CTX_use_PrivateKey_file(profile->ssl_ctx, profile->key, SSL_FILETYPE_PEM)) { - err = "PRIVATE KEY FILE ERROR"; + if (!SSL_CTX_use_PrivateKey_file(ctx, profile->key, SSL_FILETYPE_PEM)) { + *errp = "PRIVATE KEY FILE ERROR"; goto fail; } - /* verify private key */ - if ( !SSL_CTX_check_private_key(profile->ssl_ctx) ) { - err = "PRIVATE KEY FILE ERROR"; + if (!SSL_CTX_check_private_key(ctx)) { + *errp = "PRIVATE KEY FILE ERROR"; goto fail; } - SSL_CTX_set_cipher_list(profile->ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH"); + SSL_CTX_set_cipher_list(ctx, "HIGH:!DSS:!aNULL@STRENGTH"); - return 1; + return ctx; fail: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR: %s\n", err); + SSL_CTX_free(ctx); - profile->ssl_ready = 0; - verto_deinit_ssl(profile); + return NULL; +} - for (i = 0; i < profile->i; i++) { - if (profile->ip[i].secure) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL NOT ENABLED FOR LISTENER %s:%d. REVERTING TO WS\n", - profile->ip[i].local_ip, profile->ip[i].local_port); - profile->ip[i].secure = 0; +static int verto_reload_ssl(verto_profile_t *profile) +{ + const char *err = NULL; + SSL_CTX *new_ctx = verto_create_ssl_ctx(profile, &err); + + if (!new_ctx) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL reload failed for profile %s: %s\n", profile->name, err); + + return 0; + } + + SSL_CTX_free(profile->ssl_ctx); + + profile->ssl_ctx = new_ctx; + profile->ssl_ready = 1; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SSL certificates reloaded for profile %s\n", profile->name); + + return 1; +} + +static void cert_reload_handler(switch_event_t *event) +{ + verto_profile_t *p; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event received, processing\n"); + + switch_mutex_lock(verto_globals.mutex); + + for (p = verto_globals.profile_head; p; p = p->next) { + if (p->running) { + switch_thread_rwlock_wrlock(p->rwlock); + verto_reload_ssl(p); + switch_thread_rwlock_unlock(p->rwlock); } } - return 0; + switch_mutex_unlock(verto_globals.mutex); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event processed\n"); +} + +static void close_file(ks_socket_t *sock) +{ + if (*sock != KS_SOCK_INVALID) { +#ifndef WIN32 + close(*sock); +#else + closesocket(*sock); +#endif + *sock = KS_SOCK_INVALID; + } +} + +static void close_socket(ks_socket_t *sock) +{ + if (*sock != KS_SOCK_INVALID) { + shutdown(*sock, 2); + close_file(sock); + } +} + +void verto_broadcast(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id, void *user_data); + +static int verto_init_ssl(verto_profile_t *profile) +{ + const char *err = NULL; + int i = 0; + + profile->ssl_method = SSLv23_server_method(); + profile->ssl_ctx = verto_create_ssl_ctx(profile, &err); + + if (!profile->ssl_ctx) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR: %s\n", err); + + profile->ssl_ready = 0; + + for (i = 0; i < profile->i; i++) { + if (profile->ip[i].secure) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SSL NOT READY FOR LISTENER %s:%d. USE reloadcert AFTER FIXING CERTIFICATES\n", + profile->ip[i].local_ip, profile->ip[i].local_port); + } + } + + return 0; + } + + profile->ssl_ready = 1; + return 1; } @@ -1972,6 +2026,7 @@ static void client_run(jsock_t *jsock) ks_pool_open(&jsock->kpool); + switch_thread_rwlock_rdlock(jsock->profile->rwlock); #if defined(KS_VERSION_NUM) && KS_VERSION_NUM >= 20000 params = ks_json_create_object(); ks_json_add_number_to_object(params, "payload_size_max", 1000000); @@ -1979,8 +2034,10 @@ static void client_run(jsock_t *jsock) #else if (kws_init(&jsock->ws, jsock->client_socket, (jsock->ptype & PTYPE_CLIENT_SSL) ? jsock->profile->ssl_ctx : NULL, 0, flags, jsock->kpool) != KS_STATUS_SUCCESS) { #endif + switch_thread_rwlock_unlock(jsock->profile->rwlock); log_and_exit(SWITCH_LOG_NOTICE, "%s WS SETUP FAILED\n", jsock->name); } + switch_thread_rwlock_unlock(jsock->profile->rwlock); if (kws_test_flag(jsock->ws, KWS_HTTP)) { http_run(jsock); @@ -4690,7 +4747,7 @@ static int start_jsock(verto_profile_t *profile, ks_socket_t sock, int family) for (i = 0; i < profile->i; i++) { if ( profile->server_socket[i] == sock ) { - if (profile->ip[i].secure) { + if (profile->ip[i].secure && profile->ssl_ready) { ptype = PTYPE_CLIENT_SSL; } break; @@ -6887,6 +6944,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_verto_load) switch_core_register_secondary_recover_callback(modname, verto_recover_callback); + switch_event_bind(modname, SWITCH_EVENT_CERT_RELOAD, SWITCH_EVENT_SUBCLASS_ANY, cert_reload_handler, NULL); + if (verto_globals.enable_presence) { switch_event_bind(modname, SWITCH_EVENT_CHANNEL_CALLSTATE, SWITCH_EVENT_SUBCLASS_ANY, presence_event_handler, NULL); } @@ -6922,6 +6981,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_verto_shutdown) switch_core_hash_destroy(&json_GLOBALS.store_hash); switch_event_channel_unbind(NULL, verto_broadcast, NULL); + switch_event_unbind_callback(cert_reload_handler); switch_event_unbind_callback(presence_event_handler); switch_event_unbind_callback(event_handler); diff --git a/src/switch_event.c b/src/switch_event.c index 8a8c8d6c35..25bf961512 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -227,6 +227,7 @@ static char *EVENT_NAMES[] = { "DEVICE_STATE", "TEXT", "SHUTDOWN_REQUESTED", + "CERT_RELOAD", "ALL" };