summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2024-04-14 19:00:05 +0200
committerSébastien Helleu <flashcode@flashtux.org>2024-04-14 19:00:05 +0200
commit984fdb2b58bff0eeb4b4a4abe78b39ae3fd77861 (patch)
treea05a65e10d1bdba95ea8ea0cd822cee0037f86c5 /src
parenta4236be509e6a38a740b74ae1534e1c823fa8496 (diff)
downloadweechat-984fdb2b58bff0eeb4b4a4abe78b39ae3fd77861.zip
relay: add TLS connection to remote, add remote option "tls_verify"
Diffstat (limited to 'src')
-rw-r--r--src/plugins/relay/api/remote/relay-remote-network.c270
-rw-r--r--src/plugins/relay/relay-command.c23
-rw-r--r--src/plugins/relay/relay-config.c8
-rw-r--r--src/plugins/relay/relay-remote.c34
-rw-r--r--src/plugins/relay/relay-remote.h10
5 files changed, 310 insertions, 35 deletions
diff --git a/src/plugins/relay/api/remote/relay-remote-network.c b/src/plugins/relay/api/remote/relay-remote-network.c
index ff4145b8f..b076a6c43 100644
--- a/src/plugins/relay/api/remote/relay-remote-network.c
+++ b/src/plugins/relay/api/remote/relay-remote-network.c
@@ -26,7 +26,9 @@
#include <string.h>
#include <time.h>
#include <gcrypt.h>
+
#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
#include <cjson/cJSON.h>
#include "../../../weechat-plugin.h"
@@ -53,23 +55,18 @@ char *
relay_remote_network_get_url_resource (struct t_relay_remote *remote,
const char *resource)
{
- const char *ptr_url;
- char url[4096];
-
- if (!remote || !resource || !resource[0])
- return NULL;
+ char *url;
- ptr_url = weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]);
- if (!ptr_url || !ptr_url[0])
+ if (!remote || !remote->address || !resource || !resource[0])
return NULL;
- snprintf (url, sizeof (url),
- "%s%sapi/%s",
- ptr_url,
- (ptr_url[strlen (ptr_url) - 1] == '/') ? "" : "/",
- resource);
+ weechat_asprintf (&url, "%s://%s:%d/api/%s",
+ (remote->tls) ? "https" : "http",
+ remote->address,
+ remote->port,
+ resource);
- return strdup (url);
+ return url;
}
/*
@@ -932,6 +929,233 @@ relay_remote_network_connect_cb (const void *pointer, void *data, int status,
}
/*
+ * GnuTLS callback called during handshake.
+ *
+ * Returns:
+ * 0: certificate OK
+ * -1: error in certificate
+ */
+
+int
+relay_remote_network_gnutls_callback (const void *pointer, void *data,
+ gnutls_session_t tls_session,
+ const gnutls_datum_t *req_ca, int nreq,
+ const gnutls_pk_algorithm_t *pk_algos,
+ int pk_algos_len,
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */
+ gnutls_retr2_st *answer,
+#else
+ gnutls_retr_st *answer,
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */
+ int action)
+{
+ struct t_relay_remote *remote;
+ gnutls_x509_crt_t cert_temp;
+ const gnutls_datum_t *cert_list;
+ unsigned int i, cert_list_len, status;
+ time_t cert_time;
+ int rc, hostname_match, cert_temp_init;
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010706 /* 1.7.6 */
+ gnutls_datum_t cinfo;
+ int rinfo;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010706 */
+
+ /* make C compiler happy */
+ (void) data;
+ (void) req_ca;
+ (void) nreq;
+ (void) pk_algos;
+ (void) pk_algos_len;
+ (void) answer;
+
+ rc = 0;
+
+ if (!pointer)
+ return -1;
+
+ remote = (struct t_relay_remote *) pointer;
+ cert_temp_init = 0;
+ cert_list = NULL;
+ cert_list_len = 0;
+
+ if (action == WEECHAT_HOOK_CONNECT_GNUTLS_CB_VERIFY_CERT)
+ {
+ /* initialize the certificate structure */
+ if (gnutls_x509_crt_init (&cert_temp) != GNUTLS_E_SUCCESS)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: failed to initialize certificate structure"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ goto end;
+ }
+
+ /* flag to do the "deinit" (at the end of function) */
+ cert_temp_init = 1;
+
+ /* set match options */
+ hostname_match = 0;
+
+ /* get the peer's raw certificate (chain) as sent by the peer */
+ cert_list = gnutls_certificate_get_peers (tls_session, &cert_list_len);
+ if (cert_list)
+ {
+ weechat_printf (
+ NULL,
+ NG_("remote[%s]: gnutls: receiving %d certificate",
+ "remote[%s]: gnutls: receiving %d certificates",
+ cert_list_len),
+ remote->name,
+ cert_list_len);
+
+ for (i = 0; i < cert_list_len; i++)
+ {
+ if (gnutls_x509_crt_import (cert_temp,
+ &cert_list[i],
+ GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: failed to import certificate[%d]"),
+ weechat_prefix ("error"), remote->name, i + 1);
+ rc = -1;
+ goto end;
+ }
+
+ /* checks on first certificate received */
+ if (i == 0)
+ {
+ /* check if hostname matches in the first certificate */
+ if (gnutls_x509_crt_check_hostname (cert_temp,
+ remote->address) != 0)
+ {
+ hostname_match = 1;
+ }
+ }
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010706 /* 1.7.6 */
+ /* display infos about certificate */
+#if LIBGNUTLS_VERSION_NUMBER < 0x020400 /* 2.4.0 */
+ rinfo = gnutls_x509_crt_print (cert_temp,
+ GNUTLS_X509_CRT_ONELINE, &cinfo);
+#else
+ rinfo = gnutls_x509_crt_print (cert_temp,
+ GNUTLS_CRT_PRINT_ONELINE, &cinfo);
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020400 */
+ if (rinfo == 0)
+ {
+ weechat_printf (
+ NULL,
+ _("remote[%s] - certificate[%d] info:"),
+ remote->name, i + 1);
+ weechat_printf (
+ NULL,
+ "remote[%s] - %s",
+ remote->name, cinfo.data);
+ gnutls_free (cinfo.data);
+ }
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010706 */
+ /* check expiration date */
+ cert_time = gnutls_x509_crt_get_expiration_time (cert_temp);
+ if (cert_time < time (NULL))
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: certificate has expired"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ }
+ /* check activation date */
+ cert_time = gnutls_x509_crt_get_activation_time (cert_temp);
+ if (cert_time > time (NULL))
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: certificate is not yet activated"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ }
+ }
+
+ if (!hostname_match)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: the hostname in the certificate "
+ "does NOT match \"%s\""),
+ weechat_prefix ("error"), remote->name, remote->address);
+ rc = -1;
+ }
+ }
+
+ /* verify the peer’s certificate */
+ if (gnutls_certificate_verify_peers2 (tls_session, &status) < 0)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: error while checking peer's certificate"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ goto end;
+ }
+
+ /* check if certificate is trusted */
+ if (status & GNUTLS_CERT_INVALID)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: peer's certificate is NOT trusted"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ }
+ else
+ {
+ weechat_printf (
+ NULL,
+ _("remote[%s]: gnutls: peer's certificate is trusted"),
+ remote->name);
+ }
+
+ /* check if certificate issuer is known */
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: peer's certificate issuer is unknown"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ }
+
+ /* check that certificate is not revoked */
+ if (status & GNUTLS_CERT_REVOKED)
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: gnutls: the certificate has been revoked"),
+ weechat_prefix ("error"), remote->name);
+ rc = -1;
+ }
+ }
+ else if (action == WEECHAT_HOOK_CONNECT_GNUTLS_CB_SET_CERT)
+ {
+ /* nothing here */
+ }
+
+end:
+ /* an error should stop the handshake unless the user doesn't care */
+ if ((rc == -1)
+ && !weechat_config_boolean (remote->options[RELAY_REMOTE_OPTION_TLS_VERIFY]))
+ {
+ rc = 0;
+ }
+
+ if (cert_temp_init)
+ gnutls_x509_crt_deinit (cert_temp);
+
+ return rc;
+}
+
+/*
* Callback for handshake URL.
*/
@@ -953,6 +1177,8 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote = (struct t_relay_remote *)pointer;
+ remote->hook_url_handshake = NULL;
+
ptr_resp_code = weechat_hashtable_get (output, "response_code");
if (ptr_resp_code && ptr_resp_code[0] && (strcmp (ptr_resp_code, "200") != 0))
{
@@ -962,6 +1188,7 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote->name,
weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
ptr_resp_code);
+ relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
return WEECHAT_RC_OK;
}
@@ -974,6 +1201,7 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote->name,
weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
ptr_error);
+ relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
return WEECHAT_RC_OK;
}
@@ -1009,6 +1237,7 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote->name,
weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
_("hash algorithm not found"));
+ relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
return WEECHAT_RC_OK;
}
@@ -1020,6 +1249,7 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote->name,
weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
_("unknown number of hash iterations"));
+ relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
return WEECHAT_RC_OK;
}
@@ -1031,6 +1261,7 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote->name,
weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
_("unknown TOTP status"));
+ relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
return WEECHAT_RC_OK;
}
@@ -1053,10 +1284,10 @@ relay_remote_network_url_handshake_cb (const void *pointer,
remote->port,
1, /* ipv6 */
0, /* retry */
- NULL, /* gnutls_sess */
- NULL, /* gnutls_cb */
- 0, /* gnutls_dhkey_size */
- NULL, /* gnutls_priorities */
+ (remote->tls) ? &remote->gnutls_sess : NULL,
+ (remote->tls) ? &relay_remote_network_gnutls_callback : NULL,
+ 2048, /* gnutls_dhkey_size */
+ "NORMAL", /* gnutls_priorities */
NULL, /* local_hostname */
&relay_remote_network_connect_cb,
remote,
@@ -1151,6 +1382,11 @@ relay_remote_network_connect (struct t_relay_remote *remote)
"httpheader",
"Accept: application/json\n"
"Content-Type: application/json; charset=utf-8");
+ if (!weechat_config_boolean (remote->options[RELAY_REMOTE_OPTION_TLS_VERIFY]))
+ {
+ weechat_hashtable_set (options, "ssl_verifypeer", "0");
+ weechat_hashtable_set (options, "ssl_verifyhost", "0");
+ }
body = relay_remote_network_get_handshake_request ();
if (!body)
goto error;
diff --git a/src/plugins/relay/relay-command.c b/src/plugins/relay/relay-command.c
index 728f65019..8ebdf7118 100644
--- a/src/plugins/relay/relay-command.c
+++ b/src/plugins/relay/relay-command.c
@@ -400,13 +400,16 @@ relay_command_display_remote (struct t_relay_remote *remote, int with_detail)
weechat_printf (NULL, "");
weechat_printf (NULL, _("Remote: %s"), remote->name);
weechat_printf (NULL, " url. . . . . . . . . : '%s'",
- weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]));
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]));
weechat_printf (NULL, " proxy. . . . . . . . : '%s'",
- weechat_config_string (remote->options[RELAY_REMOTE_OPTION_PROXY]));
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_PROXY]));
+ weechat_printf (NULL, " tls_verify . . . . . : %s",
+ (weechat_config_string (remote->options[RELAY_REMOTE_OPTION_TLS_VERIFY])) ?
+ "on" : "off");
weechat_printf (NULL, " password . . . . . . : '%s'",
- weechat_config_string (remote->options[RELAY_REMOTE_OPTION_PASSWORD]));
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_PASSWORD]));
weechat_printf (NULL, " totp_secret. . . . . : '%s'",
- weechat_config_string (remote->options[RELAY_REMOTE_OPTION_TOTP_SECRET]));
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_TOTP_SECRET]));
}
else
{
@@ -429,7 +432,7 @@ relay_command_remote (const void *pointer, void *data,
{
struct t_relay_remote *ptr_remote, *ptr_remote2;
int i, detailed_list, one_remote_found;
- const char *ptr_proxy, *ptr_password, *ptr_totp_secret;
+ const char *ptr_proxy, *ptr_tls_verify, *ptr_password, *ptr_totp_secret;
char *remote_name;
/* make C compiler happy */
@@ -531,6 +534,7 @@ relay_command_remote (const void *pointer, void *data,
return WEECHAT_RC_OK;
}
ptr_proxy = NULL;
+ ptr_tls_verify = NULL;
ptr_password = NULL;
ptr_totp_secret = NULL;
for (i = 4; i < argc; i++)
@@ -539,6 +543,10 @@ relay_command_remote (const void *pointer, void *data,
{
ptr_proxy = argv[i] + 7;
}
+ else if (strncmp (argv[i], "-tls_verify=", 12) == 0)
+ {
+ ptr_tls_verify = argv[i] + 12;
+ }
else if (strncmp (argv[i], "-password=", 10) == 0)
{
ptr_password = argv[i] + 10;
@@ -558,7 +566,8 @@ relay_command_remote (const void *pointer, void *data,
}
}
ptr_remote = relay_remote_new (argv[2], argv[3], ptr_proxy,
- ptr_password, ptr_totp_secret);
+ ptr_tls_verify, ptr_password,
+ ptr_totp_secret);
if (ptr_remote)
{
weechat_printf (NULL, _("Remote relay \"%s\" created"), argv[2]);
@@ -846,7 +855,7 @@ relay_command_init ()
"list %(relay_remotes)"
" || listfull %(relay_remotes)"
" || add %(relay_remotes) https://localhost:9000 "
- "-password=${xxx}|-proxy=xxx|-totp_secret=${xxx}|%*"
+ "-password=${xxx}|-proxy=xxx|-tls_verify=xxx|-totp_secret=${xxx}|%*"
" || connect %(relay_remotes)"
" || send %(relay_remotes) {\"request\":\"\"}"
" || disconnect %(relay_remotes)"
diff --git a/src/plugins/relay/relay-config.c b/src/plugins/relay/relay-config.c
index d2511267a..fd4d12e16 100644
--- a/src/plugins/relay/relay-config.c
+++ b/src/plugins/relay/relay-config.c
@@ -1077,6 +1077,14 @@ relay_config_create_remote_option (const char *remote_name, int index_option,
NULL, 0, 0, value, NULL, 0,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
break;
+ case RELAY_REMOTE_OPTION_TLS_VERIFY:
+ ptr_option = weechat_config_new_option (
+ relay_config_file, relay_config_section_remote,
+ option_name, "boolean",
+ N_("check that the TLS connection is fully trusted"),
+ NULL, 0, 0, value, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ break;
case RELAY_REMOTE_OPTION_PASSWORD:
ptr_option = weechat_config_new_option (
relay_config_file, relay_config_section_remote,
diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c
index fb4072bbe..9b49bbb61 100644
--- a/src/plugins/relay/relay-remote.c
+++ b/src/plugins/relay/relay-remote.c
@@ -42,9 +42,9 @@
char *relay_remote_option_string[RELAY_REMOTE_NUM_OPTIONS] =
-{ "url", "proxy", "password", "totp_secret" };
+{ "url", "proxy", "tls_verify", "password", "totp_secret" };
char *relay_remote_option_default[RELAY_REMOTE_NUM_OPTIONS] =
-{ "", "", "", "" };
+{ "", "", "on", "", "" };
struct t_relay_remote *relay_remotes = NULL;
struct t_relay_remote *last_relay_remote = NULL;
@@ -266,20 +266,34 @@ relay_remote_get_address (const char *url)
int
relay_remote_get_port (const char *url)
{
- char *pos, *error;
+ char *pos, *pos2, *str_port, *error;
long port;
if (!url)
goto error;
- pos = strrchr (url, ':');
+ pos = strchr (url + 7, ':');
if (!pos)
goto error;
+ pos++;
+
+ pos2 = strchr (pos, '/');
+ if (pos2)
+ str_port = weechat_strndup (pos, pos2 - pos);
+ else
+ str_port = strdup (pos);
+ if (!str_port)
+ goto error;
+
error = NULL;
- port = strtol (pos + 1, &error, 10);
+ port = strtol (str_port, &error, 10);
if (error && !error[0])
+ {
+ free (str_port);
return (int)port;
+ }
+ free (str_port);
error:
return RELAY_REMOTE_DEFAULT_PORT;
@@ -403,6 +417,7 @@ relay_remote_set_url (struct t_relay_remote *remote, const char *url)
free (remote->address);
remote->address = relay_remote_get_address (url);
remote->port = relay_remote_get_port (url);
+ remote->tls = (weechat_strncmp (url, "https:", 6) == 0) ? 1 : 0;
}
/*
@@ -451,7 +466,8 @@ relay_remote_new_with_options (const char *name, struct t_config_option **option
struct t_relay_remote *
relay_remote_new (const char *name, const char *url, const char *proxy,
- const char *password, const char *totp_secret)
+ const char *tls_verify, const char *password,
+ const char *totp_secret)
{
struct t_config_option *option[RELAY_REMOTE_NUM_OPTIONS];
const char *value[RELAY_REMOTE_NUM_OPTIONS];
@@ -463,6 +479,7 @@ relay_remote_new (const char *name, const char *url, const char *proxy,
value[RELAY_REMOTE_OPTION_URL] = url;
value[RELAY_REMOTE_OPTION_PROXY] = proxy;
+ value[RELAY_REMOTE_OPTION_TLS_VERIFY] = tls_verify;
value[RELAY_REMOTE_OPTION_PASSWORD] = password;
value[RELAY_REMOTE_OPTION_TOTP_SECRET] = totp_secret;
@@ -471,7 +488,7 @@ relay_remote_new (const char *name, const char *url, const char *proxy,
option[i] = relay_config_create_remote_option (
name,
i,
- (value[i]) ? value[i] : "");
+ (value[i]) ? value[i] : relay_remote_option_string[i]);
}
new_remote = relay_remote_new_with_options (name, option);
@@ -922,6 +939,9 @@ relay_remote_print_log ()
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_URL]));
weechat_log_printf (" proxy . . . . . . . . . : '%s'",
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_PROXY]));
+ weechat_log_printf (" tls_verify. . . . . . . : %s",
+ (weechat_config_boolean (ptr_remote->options[RELAY_REMOTE_OPTION_TLS_VERIFY])) ?
+ "on" : "off");
weechat_log_printf (" password. . . . . . . . : '%s'",
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_PASSWORD]));
weechat_log_printf (" totp_secret . . . . . . : '%s'",
diff --git a/src/plugins/relay/relay-remote.h b/src/plugins/relay/relay-remote.h
index 30c408946..87db6583e 100644
--- a/src/plugins/relay/relay-remote.h
+++ b/src/plugins/relay/relay-remote.h
@@ -26,10 +26,11 @@
enum t_relay_remote_option
{
- RELAY_REMOTE_OPTION_URL = 0, /* remote URL */
- RELAY_REMOTE_OPTION_PROXY, /* proxy used for remote (optional) */
- RELAY_REMOTE_OPTION_PASSWORD, /* password for remote relay */
- RELAY_REMOTE_OPTION_TOTP_SECRET, /* TOTP secret for remote relay */
+ RELAY_REMOTE_OPTION_URL = 0, /* remote URL */
+ RELAY_REMOTE_OPTION_PROXY, /* proxy used for remote (optional) */
+ RELAY_REMOTE_OPTION_TLS_VERIFY, /* check if the connection is trusted */
+ RELAY_REMOTE_OPTION_PASSWORD, /* password for remote relay */
+ RELAY_REMOTE_OPTION_TOTP_SECRET, /* TOTP secret for remote relay */
/* number of relay remote options */
RELAY_REMOTE_NUM_OPTIONS,
};
@@ -86,6 +87,7 @@ extern struct t_relay_remote *relay_remote_new_with_options (const char *name,
struct t_config_option **options);
extern struct t_relay_remote *relay_remote_new (const char *name,
const char *proxy,
+ const char *tls_verify,
const char *url,
const char *password,
const char *totp_secret);