1
0
mirror of https://github.com/irssi/irssi.git synced 2025-02-02 15:08:01 -05:00

Merge pull request #557 from irssi/ahf/tls-pr

TLS enhancements
This commit is contained in:
ailin-nemui 2016-10-31 15:15:45 +01:00 committed by GitHub
commit 1652c8a561
30 changed files with 1106 additions and 379 deletions

37
NEWS
View File

@ -17,6 +17,43 @@ v0.8.21-head 2016-xx-xx The Irssi team <staff@irssi.org>
+ autolog_ignore_targets and activity_hide_targets learn a new syntax
tag/* and * to ignore whole networks or everything.
+ /hilight got a -matchcase flag to hilight case sensitively (#421).
+ Always build irssi with TLS support.
+ Rename SSL to TLS in the code and add -tls_* versions of the -ssl_*
options to /CONNECT and /SERVER, but make sure the -ssl_* options continue
to work.
+ Use TLS for Freenode, EFnet, EsperNet, OFTC, Rizon, and IRC6 in the default
configuration.
+ Display TLS connection information upon connect. You can disable this by
setting tls_verbose_connect to FALSE.
+ Add -tls_pinned_cert and -tls_pinned_pubkey for x509 and public key pinning.
The values needed for -tls_pinned_cert and -tls_pinned_pubkey is shown
when connecting to a TLS enabled IRC server, but you can also find the
values like this: Start by downloading the certificate from a given IRC
server:
$ openssl s_client -connect chat.freenode.net:6697 < /dev/null 2>/dev/null | \
openssl x509 > freenode.cert
Find the value for -tls_pinned_cert:
$ openssl x509 -in freenode.cert -fingerprint -sha256 -noout
Find the value for -tls_pinned_pubkey:
$ openssl x509 -in freenode.cert -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -c | \
tr a-z A-Z
+ Remove support for DANE validation of TLS certificates.
There wasn't enough support in the IRC community to push for this on the
majority of bigger IRC networks. If you believe this should be
reintroduced into irssi, then please come up with an implementation that
does not rely on the libval library. It is causing a lot of troubles for
our downstream maintainers.
- IP addresses are no longer stored when resolve_reverse_lookup is
used.
- /names and $[...] now uses utf8 string operations (#40, #411).

View File

@ -135,15 +135,6 @@ AC_ARG_WITH(perl,
fi,
want_perl=static)
AC_ARG_ENABLE(dane,
[ --enable-dane Enable DANE support],
if test x$enableval = xno ; then
want_dane=no
else
want_dane=yes
fi,
want_dane=no)
AC_ARG_ENABLE(true-color,
[ --enable-true-color Build with true color support in terminal],
if test x$enableval = xno ; then
@ -153,14 +144,6 @@ AC_ARG_ENABLE(true-color,
fi,
want_truecolor=no)
dnl **
dnl ** SSL Library checks (OpenSSL)
dnl **
AC_ARG_ENABLE(ssl,
[ --disable-ssl Disable Secure Sockets Layer support],,
enable_ssl=yes)
dnl **
dnl ** just some generic stuff...
dnl **
@ -238,6 +221,11 @@ if test "x$want_socks" = "xyes"; then
])
fi
dnl **
dnl ** OpenSSL checks
dnl **
AC_CHECK_LIB([ssl], [SSL_library_init])
dnl **
dnl ** fe-text checks
dnl **
@ -279,27 +267,7 @@ if test -z "$GLIB_LIBS"; then
AC_ERROR([GLIB is required to build irssi.])
fi
LIBS="$LIBS $GLIB_LIBS"
have_openssl=no
if test "$enable_ssl" = "yes"; then
PKG_CHECK_MODULES(SSL, openssl, :, :)
if test "$SSL_LIBS"; then
CFLAGS="$CFLAGS $SSL_CFLAGS"
have_openssl=yes
else
AC_CHECK_LIB(ssl, SSL_read, [
AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h, [
SSL_LIBS="-lssl -lcrypto"
have_openssl=yes
])
],, -lcrypto)
fi
if test "$have_openssl" = "yes"; then
AC_DEFINE(HAVE_OPENSSL,, Build with OpenSSL support)
LIBS="$LIBS $SSL_LIBS"
fi
fi
LIBS="$LIBS $GLIB_LIBS -lssl -lcrypto"
dnl **
dnl ** curses checks
@ -560,23 +528,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS"
AC_SUBST(COMMON_NOUI_LIBS)
AC_SUBST(COMMON_LIBS)
have_dane=no
if test "x$want_dane" = "xyes"; then
AC_MSG_CHECKING([for DANE])
AC_CHECK_LIB(val-threads, val_getdaneinfo,
[
LIBS="$LIBS -lval-threads -lsres"
AC_DEFINE([HAVE_DANE], [], [DANE support])
have_dane=yes
], [], [-lssl -lcrypto -lsres -lpthread])
if test x$have_dane = "xyes" ; then
if test x$have_openssl = "xno" ; then
AC_ERROR([SSL is required to build Irssi with DANE support enabled.])
fi
fi
fi
if test "x$want_truecolor" = "xyes"; then
AC_DEFINE([TERM_TRUECOLOR], [], [true color support in terminal])
else
@ -695,18 +646,7 @@ echo "Install prefix ................... : $prefix"
echo
echo "Building with SSL support ........ : $have_openssl"
if test "x$have_openssl" = "xno" -a "x$enable_ssl" = "xyes"; then
if test -f /etc/debian_version; then
echo " - Try: sudo apt-get install libssl-dev"
elif test -f /etc/redhat-release; then
echo " - Try installing openssl-devel"
else
echo " - Try installing OpenSSL development headers"
fi
fi
echo "Building with 64bit DCC support .. : $offt_64bit"
echo "Building with DANE support ....... : $have_dane"
echo "Building with true color support.. : $want_truecolor"
echo

View File

@ -5,23 +5,24 @@
%9Parameters:%9
-4: Connects using IPv4.
-6: Connects using IPv6.
-ssl: Connects using SSL encryption.
-ssl_cert: The SSL client certificate file.
-ssl_pkey: The SSL client private key, if not included in the
certificate file.
-ssl_pass: The password for the SSL client private key or certificate.
-ssl_verify: Verifies the SSL certificate of the server.
-ssl_cafile: The file with the list of CA certificates.
-ssl_capath: The directory which contains the CA certificates.
-ssl_ciphers: SSL cipher suite preference lists.
-noproxy: Ignores the global proxy configuration.
-network: The network this connection belongs to.
-host: The hostname you would like to connect from.
-rawlog: Immediately open rawlog after connecting.
-!: Doesn't autojoin channels.
-noautosendcmd: Doesn't execute autosendcmd.
-4: Connects using IPv4.
-6: Connects using IPv6.
-tls: Connects using TLS encryption.
-tls_cert: The TLS client certificate file.
-tls_pkey: The TLS client private key, if not included in the certificate file.
-tls_pass: The password for the TLS client private key or certificate.
-tls_verify: Verifies the TLS certificate of the server.
-tls_cafile: The file with the list of CA certificates.
-tls_capath: The directory which contains the CA certificates.
-tls_ciphers: TLS cipher suite preference lists.
-tls_pinned_cert: Pinned x509 certificate fingerprint.
-tls_pinned_pubkey: Pinned public key fingerprint.
-noproxy: Ignores the global proxy configuration.
-network: The network this connection belongs to.
-host: The hostname you would like to connect from.
-rawlog: Immediately open rawlog after connecting.
-!: Doesn't autojoin channels.
-noautosendcmd: Doesn't execute autosendcmd.
A network or server to connect to; you can optionally specify a custom port,
password and nickname.

View File

@ -5,45 +5,47 @@
%9Parameters:%9
LIST: Displays the list of servers you are connected to.
CONNECT: Connects to the given server.
ADD: Adds a server to your configuration.
MODIFY: Modifies a server in your configuration.
REMOVE: Removes a server from your configuration.
PURGE: Purges the commands queued to be sent to the server.
LIST: Displays the list of servers you are connected to.
CONNECT: Connects to the given server.
ADD: Adds a server to your configuration.
MODIFY: Modifies a server in your configuration.
REMOVE: Removes a server from your configuration.
PURGE: Purges the commands queued to be sent to the server.
-!: Doesn't autojoin the channels.
-4: Connects using IPv4.
-6: Connects using IPv6.
-ssl: Connects using SSL encryption.
-ssl_cert: The SSL client certificate file.
-ssl_pkey: The SSL client private key, if not included in the
certificate file.
-ssl_pass: The password for the SSL client private key or certificate.
-ssl_verify: Verifies the SSL certificate of the server.
-ssl_cafile: The file with the list of CA certificates.
-ssl_capath: The directory which contains the CA certificates.
-ssl_ciphers: SSL cipher suite preference lists.
-auto: Automatically connects to the server on startup.
-noauto: Doesn't connect to the server on startup.
-network: The network the server belongs to.
-host: The hostname you would like to connect from.
-cmdspeed: Specifies the minimum amount of time, expressed in
milliseconds, that the client must wait before sending
additional commands to the server.
-cmdmax: Specifies the maximum number of commands to perform
before starting the internal flood protection.
-port: Specifies the port to connect to the server.
-noproxy: Ignores the global proxy configuration.
-rawlog: Immediately open rawlog after connecting.
-noautosendcmd: Doesn't execute autosendcmd.
-!: Doesn't autojoin the channels.
-4: Connects using IPv4.
-6: Connects using IPv6.
-tls: Connects using TLS encryption.
-tls_cert: The TLS client certificate file.
-tls_pkey: The TLS client private key, if not included in the
certificate file.
-tls_pass: The password for the TLS client private key or certificate.
-tls_verify: Verifies the TLS certificate of the server.
-tls_cafile: The file with the list of CA certificates.
-tls_capath: The directory which contains the CA certificates.
-tls_ciphers: TLS cipher suite preference lists.
-tls_pinned_cert: Pinned x509 certificate fingerprint.
-tls_pinned_pubkey: Pinned public key fingerprint.
-auto: Automatically connects to the server on startup.
-noauto: Doesn't connect to the server on startup.
-network: The network the server belongs to.
-host: The hostname you would like to connect from.
-cmdspeed: Specifies the minimum amount of time, expressed in
milliseconds, that the client must wait before sending
additional commands to the server.
-cmdmax: Specifies the maximum number of commands to perform
before starting the internal flood protection.
-port: Specifies the port to connect to the server.
-noproxy: Ignores the global proxy configuration.
-rawlog: Immediately open rawlog after connecting.
-noautosendcmd: Doesn't execute autosendcmd.
The server, port and network to add, modify or remove; if no argument is
given, the list of servers you are connected to will be returned.
%9Description:%9
Displays, adds, modifies or removes the network configuration of IRC
Displays, adds, modifies or removes the network configuration of IRC
servers.
When using the ADD parameter on a server that already exists, the

View File

@ -56,9 +56,7 @@ modules.c:
"module error", int error, char *text, char *rootmodule, char *submodule
network-openssl.c:
"tlsa available", SERVER_REC
"tlsa verification success", SERVER_REC
"tlsa verification failed", SERVER_REC
"tls handshake finished", SERVER_REC, TLS_REC
nicklist.c:
"nicklist new", CHANNEL_REC, NICK_REC

View File

@ -1,16 +1,16 @@
servers = (
{ address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; },
{ address = "irc.efnet.org"; chatnet = "EFNet"; port = "6667"; },
{ address = "irc.esper.net"; chatnet = "EsperNet"; port = "6667"; },
{ address = "chat.freenode.net"; chatnet = "Freenode"; port = "6667"; },
{ address = "ssl.efnet.org"; chatnet = "EFNet"; port = "9999"; use_tls = "yes"; },
{ address = "irc.esper.net"; chatnet = "EsperNet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "chat.freenode.net"; chatnet = "Freenode"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "irc.gamesurge.net"; chatnet = "GameSurge"; port = "6667"; },
{ address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; },
{ address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; use_tls = "yes"; },
{ address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; },
{ address = "irc.ircsource.net"; chatnet = "IRCSource"; port = "6667"; },
{ address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; },
{ address = "irc.oftc.net"; chatnet = "OFTC"; port = "6667"; },
{ address = "irc.oftc.net"; chatnet = "OFTC"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "irc.quakenet.org"; chatnet = "QuakeNet"; port = "6667"; },
{ address = "irc.rizon.net"; chatnet = "Rizon"; port = "6667"; },
{ address = "irc.rizon.net"; chatnet = "Rizon"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; },
{ address = "irc.undernet.org"; chatnet = "Undernet"; port = "6667"; }
);

View File

@ -46,6 +46,7 @@ libcore_a_SOURCES = \
special-vars.c \
utf8.c \
wcwidth.c \
tls.c \
write-buffer.c
structure_headers = \
@ -97,5 +98,6 @@ pkginc_core_HEADERS = \
special-vars.h \
utf8.h \
window-item-def.h \
tls.h \
write-buffer.h \
$(structure_headers)

View File

@ -99,27 +99,31 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
else if (g_hash_table_lookup(optlist, "4") != NULL)
conn->family = AF_INET;
if (g_hash_table_lookup(optlist, "ssl") != NULL)
conn->use_ssl = TRUE;
if ((tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
conn->ssl_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
conn->ssl_pkey = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
conn->ssl_pass = g_strdup(tmp);
if (g_hash_table_lookup(optlist, "ssl_verify") != NULL)
conn->ssl_verify = TRUE;
if ((tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
conn->ssl_cafile = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
conn->ssl_capath = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
conn->ssl_ciphers = g_strdup(tmp);
if ((conn->ssl_capath != NULL && conn->ssl_capath[0] != '\0')
|| (conn->ssl_cafile != NULL && conn->ssl_cafile[0] != '\0'))
conn->ssl_verify = TRUE;
if ((conn->ssl_cert != NULL && conn->ssl_cert[0] != '\0') || conn->ssl_verify)
conn->use_ssl = TRUE;
if (g_hash_table_lookup(optlist, "tls") != NULL || g_hash_table_lookup(optlist, "ssl") != NULL)
conn->use_tls = TRUE;
if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
conn->tls_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
conn->tls_pkey = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
conn->tls_pass = g_strdup(tmp);
if (g_hash_table_lookup(optlist, "tls_verify") != NULL || g_hash_table_lookup(optlist, "ssl_verify") != NULL)
conn->tls_verify = TRUE;
if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
conn->tls_cafile = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
conn->tls_capath = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
conn->tls_ciphers = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
conn->tls_pinned_cert = g_strdup(tmp);
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
conn->tls_pinned_pubkey = g_strdup(tmp);
if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
|| (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
conn->tls_verify = TRUE;
if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify)
conn->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
@ -494,7 +498,7 @@ void chat_commands_init(void)
signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +host noproxy -rawlog noautosendcmd");
command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_pinned_cert +ssl_pinned_pubkey tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd");
command_set_options("msg", "channel nick");
}

View File

@ -930,3 +930,23 @@ char **strsplit_len(const char *str, int len, gboolean onspace)
return ret;
}
char *binary_to_hex(unsigned char *buffer, size_t size)
{
static const char hex[] = "0123456789ABCDEF";
char *result = NULL;
int i;
if (buffer == NULL || size == 0)
return NULL;
result = g_malloc(3 * size);
for (i = 0; i < size; i++) {
result[i * 3 + 0] = hex[(buffer[i] >> 4) & 0xf];
result[i * 3 + 1] = hex[(buffer[i] >> 0) & 0xf];
result[i * 3 + 2] = i == size - 1 ? '\0' : ':';
}
return result;
}

View File

@ -109,4 +109,8 @@ int find_substr(const char *list, const char *item);
/* split `str' into `len' sized substrings */
char **strsplit_len(const char *str, int len, gboolean onspace);
/* Convert a given buffer to a printable, colon-delimited, hex string and
* return a pointer to the newly allocated buffer */
char *binary_to_hex(unsigned char *buffer, size_t size);
#endif

View File

@ -22,8 +22,8 @@
#include "network.h"
#include "misc.h"
#include "servers.h"
#ifdef HAVE_OPENSSL
#include "signals.h"
#include "tls.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
@ -32,11 +32,6 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifdef HAVE_DANE
#include <validator/validator.h>
#include <validator/val_dane.h>
#endif
/* ssl i/o channel object */
typedef struct
{
@ -203,78 +198,13 @@ static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname)
return matched;
}
static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server)
static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server, TLS_REC *tls_rec)
{
long result;
#ifdef HAVE_DANE
int dane_ret;
struct val_daneparams daneparams;
struct val_danestatus *danestatus = NULL;
// Check if a TLSA record is available.
daneparams.port = port;
daneparams.proto = DANE_PARAM_PROTO_TCP;
dane_ret = val_getdaneinfo(NULL, hostname, &daneparams, &danestatus);
if (dane_ret == VAL_DANE_NOERROR) {
signal_emit("tlsa available", 1, server);
}
if (danestatus != NULL) {
int do_certificate_check = 1;
if (val_dane_check(NULL, ssl, danestatus, &do_certificate_check) != VAL_DANE_NOERROR) {
g_warning("DANE: TLSA record for hostname %s port %d could not be verified", hostname, port);
signal_emit("tlsa verification failed", 1, server);
val_free_dane(danestatus);
return FALSE;
}
signal_emit("tlsa verification success", 1, server);
val_free_dane(danestatus);
if (do_certificate_check == 0) {
return TRUE;
}
}
#endif
result = SSL_get_verify_result(ssl);
if (result != X509_V_OK) {
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int n;
char *str;
g_warning("Could not verify SSL servers certificate: %s",
X509_verify_cert_error_string(result));
if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
g_warning(" Could not get subject-name from peer certificate");
else {
g_warning(" Subject : %s", str);
free(str);
}
if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
g_warning(" Could not get issuer-name from peer certificate");
else {
g_warning(" Issuer : %s", str);
free(str);
}
if (! X509_digest(cert, EVP_md5(), md, &n))
g_warning(" Could not get fingerprint from peer certificate");
else {
char hex[] = "0123456789ABCDEF";
char fp[EVP_MAX_MD_SIZE*3];
if (n < sizeof(fp)) {
unsigned int i;
for (i = 0; i < n; i++) {
fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
fp[i*3+2] = i == n - 1 ? '\0' : ':';
}
g_warning(" MD5 Fingerprint : %s", fp);
}
}
g_warning("Could not verify TLS servers certificate: %s", X509_verify_cert_error_string(result));
return FALSE;
} else if (! irssi_ssl_verify_hostname(cert, hostname)){
return FALSE;
@ -457,13 +387,13 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
SSL *ssl;
SSL_CTX *ctx = NULL;
const char *mycert = server->connrec->ssl_cert;
const char *mypkey = server->connrec->ssl_pkey;
const char *mypass = server->connrec->ssl_pass;
const char *cafile = server->connrec->ssl_cafile;
const char *capath = server->connrec->ssl_capath;
const char *ciphers = server->connrec->ssl_ciphers;
gboolean verify = server->connrec->ssl_verify;
const char *mycert = server->connrec->tls_cert;
const char *mypkey = server->connrec->tls_pkey;
const char *mypass = server->connrec->tls_pass;
const char *cafile = server->connrec->tls_cafile;
const char *capath = server->connrec->tls_capath;
const char *ciphers = server->connrec->tls_ciphers;
gboolean verify = server->connrec->tls_verify;
g_return_val_if_fail(handle != NULL, NULL);
@ -482,7 +412,8 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_default_passwd_cb(ctx, get_pem_password_callback);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)mypass);
if (ciphers && *ciphers) {
if (ciphers != NULL && ciphers[0] != '\0') {
if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1)
g_warning("No valid SSL cipher suite could be selected");
}
@ -511,7 +442,7 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
if (capath && *capath)
scapath = convert_home(capath);
if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) {
g_warning("Could not load CA list for verifying SSL server certificate");
g_warning("Could not load CA list for verifying TLS server certificate");
g_free(scafile);
g_free(scapath);
SSL_CTX_free(ctx);
@ -565,6 +496,192 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return gchan;
}
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));
}
static void set_pubkey_info(TLS_REC *tls, X509 *cert, unsigned char *cert_fingerprint, size_t cert_fingerprint_size, unsigned char *public_key_fingerprint, size_t public_key_fingerprint_size)
{
g_return_if_fail(tls != NULL);
g_return_if_fail(cert != NULL);
EVP_PKEY *pubkey = NULL;
char *cert_fingerprint_hex = NULL;
char *public_key_fingerprint_hex = NULL;
BIO *bio = NULL;
char buffer[128];
size_t length;
pubkey = X509_get_pubkey(cert);
cert_fingerprint_hex = binary_to_hex(cert_fingerprint, cert_fingerprint_size);
tls_rec_set_certificate_fingerprint(tls, cert_fingerprint_hex);
tls_rec_set_certificate_fingerprint_algorithm(tls, "SHA256");
// Show algorithm.
switch (EVP_PKEY_id(pubkey)) {
case EVP_PKEY_RSA:
tls_rec_set_public_key_algorithm(tls, "RSA");
break;
case EVP_PKEY_DSA:
tls_rec_set_public_key_algorithm(tls, "DSA");
break;
case EVP_PKEY_EC:
tls_rec_set_public_key_algorithm(tls, "EC");
break;
default:
tls_rec_set_public_key_algorithm(tls, "Unknown");
break;
}
public_key_fingerprint_hex = binary_to_hex(public_key_fingerprint, public_key_fingerprint_size);
tls_rec_set_public_key_fingerprint(tls, public_key_fingerprint_hex);
tls_rec_set_public_key_size(tls, EVP_PKEY_bits(pubkey));
tls_rec_set_public_key_fingerprint_algorithm(tls, "SHA256");
// Read the NotBefore timestamp.
bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notBefore(cert));
length = BIO_read(bio, buffer, sizeof(buffer));
buffer[length] = '\0';
BIO_free(bio);
tls_rec_set_not_before(tls, buffer);
// Read the NotAfter timestamp.
bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notAfter(cert));
length = BIO_read(bio, buffer, sizeof(buffer));
buffer[length] = '\0';
BIO_free(bio);
tls_rec_set_not_after(tls, buffer);
g_free(cert_fingerprint_hex);
g_free(public_key_fingerprint_hex);
EVP_PKEY_free(pubkey);
}
static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl)
{
g_return_if_fail(tls != NULL);
g_return_if_fail(ssl != NULL);
int nid;
char *key = NULL;
char *value = NULL;
STACK_OF(X509) *chain = NULL;
int i;
int j;
TLS_CERT_REC *cert_rec = NULL;
X509_NAME *name = NULL;
X509_NAME_ENTRY *entry = NULL;
TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL;
ASN1_STRING *data = NULL;
chain = SSL_get_peer_cert_chain(ssl);
if (chain == NULL)
return;
for (i = 0; i < sk_X509_num(chain); i++) {
cert_rec = tls_cert_create_rec();
// Subject.
name = X509_get_subject_name(sk_X509_value(chain, i));
for (j = 0; j < X509_NAME_entry_count(name); j++) {
entry = X509_NAME_get_entry(name, j);
nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
key = (char *)OBJ_nid2sn(nid);
if (key == NULL)
key = (char *)OBJ_nid2ln(nid);
data = X509_NAME_ENTRY_get_data(entry);
value = (char *)ASN1_STRING_data(data);
tls_cert_entry_rec = tls_cert_entry_create_rec(key, value);
tls_cert_rec_append_subject_entry(cert_rec, tls_cert_entry_rec);
}
// Issuer.
name = X509_get_issuer_name(sk_X509_value(chain, i));
for (j = 0; j < X509_NAME_entry_count(name); j++) {
entry = X509_NAME_get_entry(name, j);
nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
key = (char *)OBJ_nid2sn(nid);
if (key == NULL)
key = (char *)OBJ_nid2ln(nid);
data = X509_NAME_ENTRY_get_data(entry);
value = (char *)ASN1_STRING_data(data);
tls_cert_entry_rec = tls_cert_entry_create_rec(key, value);
tls_cert_rec_append_issuer_entry(cert_rec, tls_cert_entry_rec);
}
tls_rec_append_cert(tls, cert_rec);
}
}
static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl)
{
g_return_if_fail(tls != NULL);
g_return_if_fail(ssl != NULL);
#ifdef SSL_get_server_tmp_key
// Show ephemeral key information.
EVP_PKEY *ephemeral_key = NULL;
EC_KEY *ec_key = NULL;
char *ephemeral_key_algorithm = NULL;
char *cname = NULL;
int nid;
if (SSL_get_server_tmp_key(ssl, &ephemeral_key)) {
switch (EVP_PKEY_id(ephemeral_key)) {
case EVP_PKEY_DH:
tls_rec_set_ephemeral_key_algorithm(tls, "DH");
tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
break;
case EVP_PKEY_EC:
ec_key = EVP_PKEY_get1_EC_KEY(ephemeral_key);
nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
EC_KEY_free(ec_key);
cname = (char *)OBJ_nid2sn(nid);
ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname);
tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm);
tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
g_free_and_null(ephemeral_key_algorithm);
break;
default:
tls_rec_set_ephemeral_key_algorithm(tls, "Unknown");
tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
break;
}
EVP_PKEY_free(ephemeral_key);
}
#endif // SSL_get_server_tmp_key.
}
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
{
GIOChannel *handle, *ssl_handle;
@ -582,8 +699,19 @@ int irssi_ssl_handshake(GIOChannel *handle)
{
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
int ret, err;
X509 *cert;
const char *errstr;
const char *errstr = NULL;
X509 *cert = NULL;
X509_PUBKEY *pubkey = NULL;
int pubkey_size = 0;
unsigned char *pubkey_der = NULL;
unsigned char *pubkey_der_tmp = NULL;
unsigned char pubkey_fingerprint[EVP_MAX_MD_SIZE];
unsigned int pubkey_fingerprint_size;
unsigned char cert_fingerprint[EVP_MAX_MD_SIZE];
unsigned int cert_fingerprint_size;
const char *pinned_cert_fingerprint = chan->server->connrec->tls_pinned_cert;
const char *pinned_pubkey_fingerprint = chan->server->connrec->tls_pinned_pubkey;
TLS_REC *tls = NULL;
ERR_clear_error();
ret = SSL_connect(chan->ssl);
@ -611,22 +739,74 @@ int irssi_ssl_handshake(GIOChannel *handle)
}
cert = SSL_get_peer_certificate(chan->ssl);
pubkey = X509_get_X509_PUBKEY(cert);
if (cert == NULL) {
g_warning("SSL server supplied no certificate");
return -1;
g_warning("TLS server supplied no certificate");
ret = 0;
goto done;
}
ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server);
if (pubkey == NULL) {
g_warning("TLS server supplied no certificate public key");
ret = 0;
goto done;
}
if (! X509_digest(cert, EVP_sha256(), cert_fingerprint, &cert_fingerprint_size)) {
g_warning("Unable to generate certificate fingerprint");
ret = 0;
goto done;
}
pubkey_size = i2d_X509_PUBKEY(pubkey, NULL);
pubkey_der = pubkey_der_tmp = g_new(unsigned char, pubkey_size);
i2d_X509_PUBKEY(pubkey, &pubkey_der_tmp);
EVP_Digest(pubkey_der, pubkey_size, pubkey_fingerprint, &pubkey_fingerprint_size, EVP_sha256(), 0);
tls = tls_create_rec();
set_cipher_info(tls, chan->ssl);
set_pubkey_info(tls, cert, cert_fingerprint, cert_fingerprint_size, pubkey_fingerprint, pubkey_fingerprint_size);
set_peer_cert_chain_info(tls, chan->ssl);
set_server_temporary_key_info(tls, chan->ssl);
// Emit the TLS rec.
signal_emit("tls handshake finished", 2, chan->server, tls);
ret = 1;
if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') {
ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0;
if (! ret) {
g_warning(" Pinned certificate mismatch");
goto done;
}
}
if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') {
ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0;
if (! ret) {
g_warning(" Pinned public key mismatch");
goto done;
}
}
if (chan->verify) {
ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls);
if (! ret) {
// irssi_ssl_verify emits a warning itself.
goto done;
}
}
done:
tls_rec_free(tls);
X509_free(cert);
g_free(pubkey_der);
return ret ? 0 : -1;
}
#else /* HAVE_OPENSSL */
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
{
g_warning("Connection failed: SSL support not enabled in this build.");
errno = ENOSYS;
return NULL;
}
#endif /* ! HAVE_OPENSSL */

View File

@ -23,12 +23,14 @@ char *nick;
char *username;
char *realname;
char *ssl_cert;
char *ssl_pkey;
char *ssl_pass;
char *ssl_cafile;
char *ssl_capath;
char *ssl_ciphers;
char *tls_cert;
char *tls_pkey;
char *tls_pass;
char *tls_cafile;
char *tls_capath;
char *tls_ciphers;
char *tls_pinned_cert;
char *tls_pinned_pubkey;
GIOChannel *connect_handle; /* connect using this handle */
@ -38,8 +40,8 @@ unsigned int reconnecting:1; /* we're trying to reconnect any connection */
unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
unsigned int no_autosendcmd:1; /* don't execute autosendcmd */
unsigned int unix_socket:1; /* Connect using named unix socket */
unsigned int use_ssl:1; /* this connection uses SSL */
unsigned int ssl_verify:1;
unsigned int use_tls:1; /* this connection uses TLS */
unsigned int tls_verify:1;
unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */
char *channels;
char *away_reason;

View File

@ -11,12 +11,14 @@ char *password;
int sasl_mechanism;
char *sasl_password;
char *ssl_cert;
char *ssl_pkey;
char *ssl_pass;
char *ssl_cafile;
char *ssl_capath;
char *ssl_ciphers;
char *tls_cert;
char *tls_pkey;
char *tls_pass;
char *tls_cafile;
char *tls_capath;
char *tls_ciphers;
char *tls_pinned_cert;
char *tls_pinned_pubkey;
char *own_host; /* address to use when connecting this server */
IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
@ -28,7 +30,7 @@ unsigned int no_proxy:1;
unsigned int last_failed:1; /* if last connection attempt failed */
unsigned int banned:1; /* if we're banned from this server */
unsigned int dns_error:1; /* DNS said the host doesn't exist */
unsigned int use_ssl:1; /* this connection uses SSL */
unsigned int ssl_verify:1;
unsigned int use_tls:1; /* this connection uses TLS */
unsigned int tls_verify:1;
GHashTable *module_data;

View File

@ -192,13 +192,15 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
dest->no_autosendcmd = src->no_autosendcmd;
dest->unix_socket = src->unix_socket;
dest->use_ssl = src->use_ssl;
dest->ssl_cert = g_strdup(src->ssl_cert);
dest->ssl_pkey = g_strdup(src->ssl_pkey);
dest->ssl_verify = src->ssl_verify;
dest->ssl_cafile = g_strdup(src->ssl_cafile);
dest->ssl_capath = g_strdup(src->ssl_capath);
dest->ssl_ciphers = g_strdup(src->ssl_ciphers);
dest->use_tls = src->use_tls;
dest->tls_cert = g_strdup(src->tls_cert);
dest->tls_pkey = g_strdup(src->tls_pkey);
dest->tls_verify = src->tls_verify;
dest->tls_cafile = g_strdup(src->tls_cafile);
dest->tls_capath = g_strdup(src->tls_capath);
dest->tls_ciphers = g_strdup(src->tls_ciphers);
dest->tls_pinned_cert = g_strdup(src->tls_pinned_cert);
dest->tls_pinned_pubkey = g_strdup(src->tls_pinned_pubkey);
return dest;
}

View File

@ -167,20 +167,24 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
if (sserver->port > 0 && conn->port <= 0)
conn->port = sserver->port;
conn->use_ssl = sserver->use_ssl;
if (conn->ssl_cert == NULL && sserver->ssl_cert != NULL && sserver->ssl_cert[0] != '\0')
conn->ssl_cert = g_strdup(sserver->ssl_cert);
if (conn->ssl_pkey == NULL && sserver->ssl_pkey != NULL && sserver->ssl_pkey[0] != '\0')
conn->ssl_pkey = g_strdup(sserver->ssl_pkey);
if (conn->ssl_pass == NULL && sserver->ssl_pass != NULL && sserver->ssl_pass[0] != '\0')
conn->ssl_pass = g_strdup(sserver->ssl_pass);
conn->ssl_verify = sserver->ssl_verify;
if (conn->ssl_cafile == NULL && sserver->ssl_cafile != NULL && sserver->ssl_cafile[0] != '\0')
conn->ssl_cafile = g_strdup(sserver->ssl_cafile);
if (conn->ssl_capath == NULL && sserver->ssl_capath != NULL && sserver->ssl_capath[0] != '\0')
conn->ssl_capath = g_strdup(sserver->ssl_capath);
if (conn->ssl_ciphers == NULL && sserver->ssl_ciphers != NULL && sserver->ssl_ciphers[0] != '\0')
conn->ssl_ciphers = g_strdup(sserver->ssl_ciphers);
conn->use_tls = sserver->use_tls;
if (conn->tls_cert == NULL && sserver->tls_cert != NULL && sserver->tls_cert[0] != '\0')
conn->tls_cert = g_strdup(sserver->tls_cert);
if (conn->tls_pkey == NULL && sserver->tls_pkey != NULL && sserver->tls_pkey[0] != '\0')
conn->tls_pkey = g_strdup(sserver->tls_pkey);
if (conn->tls_pass == NULL && sserver->tls_pass != NULL && sserver->tls_pass[0] != '\0')
conn->tls_pass = g_strdup(sserver->tls_pass);
conn->tls_verify = sserver->tls_verify;
if (conn->tls_cafile == NULL && sserver->tls_cafile != NULL && sserver->tls_cafile[0] != '\0')
conn->tls_cafile = g_strdup(sserver->tls_cafile);
if (conn->tls_capath == NULL && sserver->tls_capath != NULL && sserver->tls_capath[0] != '\0')
conn->tls_capath = g_strdup(sserver->tls_capath);
if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0')
conn->tls_ciphers = g_strdup(sserver->tls_ciphers);
if (conn->tls_pinned_cert == NULL && sserver->tls_pinned_cert != NULL && sserver->tls_pinned_cert[0] != '\0')
conn->tls_pinned_cert = g_strdup(sserver->tls_pinned_cert);
if (conn->tls_pinned_pubkey == NULL && sserver->tls_pinned_pubkey != NULL && sserver->tls_pinned_pubkey[0] != '\0')
conn->tls_pinned_pubkey = g_strdup(sserver->tls_pinned_pubkey);
server_setup_fill_reconn(conn, sserver);
@ -362,9 +366,10 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port,
static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
{
SERVER_SETUP_REC *rec;
CHATNET_REC *chatnetrec;
CHATNET_REC *chatnetrec;
char *server, *chatnet, *family;
int port;
char *value = NULL;
g_return_val_if_fail(node != NULL, NULL);
@ -390,7 +395,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
chatnet_create(chatnetrec);
}
family = config_node_get_str(node, "family", "");
family = config_node_get_str(node, "family", "");
rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
rec->type = module_get_uniq_id("SERVER SETUP", 0);
@ -400,18 +405,55 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
(g_ascii_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
rec->address = g_strdup(server);
rec->password = g_strdup(config_node_get_str(node, "password", NULL));
rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE);
rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL));
rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL));
rec->ssl_pass = g_strdup(config_node_get_str(node, "ssl_pass", NULL));
rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE);
rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL));
rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL));
rec->ssl_ciphers = g_strdup(config_node_get_str(node, "ssl_ciphers", NULL));
if (rec->ssl_cafile || rec->ssl_capath)
rec->ssl_verify = TRUE;
if (rec->ssl_cert != NULL || rec->ssl_verify)
rec->use_ssl = TRUE;
rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE);
rec->tls_verify = config_node_get_bool(node, "tls_verify", FALSE) || config_node_get_bool(node, "ssl_verify", FALSE);
value = config_node_get_str(node, "tls_cert", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_cert", NULL);
rec->tls_cert = g_strdup(value);
value = config_node_get_str(node, "tls_pkey", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_pkey", NULL);
rec->tls_pkey = g_strdup(value);
value = config_node_get_str(node, "tls_pass", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_pass", NULL);
rec->tls_pass = g_strdup(value);
value = config_node_get_str(node, "tls_cafile", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_cafile", NULL);
rec->tls_cafile = g_strdup(value);
value = config_node_get_str(node, "tls_capath", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_capath", NULL);
rec->tls_capath = g_strdup(value);
value = config_node_get_str(node, "tls_ciphers", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_ciphers", NULL);
rec->tls_ciphers = g_strdup(value);
value = config_node_get_str(node, "tls_pinned_cert", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_pinned_cert", NULL);
rec->tls_pinned_cert = g_strdup(value);
value = config_node_get_str(node, "tls_pinned_pubkey", NULL);
if (value == NULL)
value = config_node_get_str(node, "ssl_pinned_pubkey", NULL);
rec->tls_pinned_pubkey = g_strdup(value);
if (rec->tls_cafile || rec->tls_capath)
rec->tls_verify = TRUE;
if (rec->tls_cert != NULL || rec->tls_verify)
rec->use_tls = TRUE;
rec->port = port;
rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
@ -463,14 +505,18 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
iconfig_node_set_int(node, "port", rec->port);
iconfig_node_set_str(node, "password", rec->password);
iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
iconfig_node_set_str(node, "ssl_pass", rec->ssl_pass);
iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
iconfig_node_set_str(node, "ssl_ciphers", rec->ssl_ciphers);
iconfig_node_set_bool(node, "use_tls", rec->use_tls);
iconfig_node_set_str(node, "tls_cert", rec->tls_cert);
iconfig_node_set_str(node, "tls_pkey", rec->tls_pkey);
iconfig_node_set_str(node, "tls_pass", rec->tls_pass);
iconfig_node_set_bool(node, "tls_verify", rec->tls_verify);
iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile);
iconfig_node_set_str(node, "tls_capath", rec->tls_capath);
iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers);
iconfig_node_set_str(node, "tls_pinned_cert", rec->tls_pinned_cert);
iconfig_node_set_str(node, "tls_pinned_pubkey", rec->tls_pinned_pubkey);
iconfig_node_set_str(node, "own_host", rec->own_host);
iconfig_node_set_str(node, "family",
@ -514,12 +560,14 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec)
g_free_not_null(rec->own_ip6);
g_free_not_null(rec->chatnet);
g_free_not_null(rec->password);
g_free_not_null(rec->ssl_cert);
g_free_not_null(rec->ssl_pkey);
g_free_not_null(rec->ssl_pass);
g_free_not_null(rec->ssl_cafile);
g_free_not_null(rec->ssl_capath);
g_free_not_null(rec->ssl_ciphers);
g_free_not_null(rec->tls_cert);
g_free_not_null(rec->tls_pkey);
g_free_not_null(rec->tls_pass);
g_free_not_null(rec->tls_cafile);
g_free_not_null(rec->tls_capath);
g_free_not_null(rec->tls_ciphers);
g_free_not_null(rec->tls_pinned_cert);
g_free_not_null(rec->tls_pinned_pubkey);
g_free(rec->address);
g_free(rec);
}

View File

@ -167,7 +167,6 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
server_connect_finished(server);
}
#ifdef HAVE_OPENSSL
static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle)
{
int error;
@ -198,7 +197,6 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han
server_connect_finished(server);
}
#endif
static void server_real_connect(SERVER_REC *server, IPADDR *ip,
const char *unix_socket)
@ -221,7 +219,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4;
port = server->connrec->proxy != NULL ?
server->connrec->proxy_port : server->connrec->port;
handle = server->connrec->use_ssl ?
handle = server->connrec->use_tls ?
net_connect_ip_ssl(ip, port, own_ip, server) : net_connect_ip(ip, port, own_ip);
} else {
handle = net_connect_unix(unix_socket);
@ -239,7 +237,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
}
server->no_reconnect = TRUE;
}
if (server->connrec->use_ssl && errno == ENOSYS)
if (server->connrec->use_tls && errno == ENOSYS)
server->no_reconnect = TRUE;
server->connection_lost = TRUE;
@ -247,11 +245,9 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
g_free(errmsg2);
} else {
server->handle = net_sendbuffer_create(handle, 0);
#ifdef HAVE_OPENSSL
if (server->connrec->use_ssl)
if (server->connrec->use_tls)
server_connect_callback_init_ssl(server, handle);
else
#endif
server->connect_tag =
g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
(GInputFunction)
@ -626,22 +622,24 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
g_free_not_null(conn->own_ip4);
g_free_not_null(conn->own_ip6);
g_free_not_null(conn->password);
g_free_not_null(conn->nick);
g_free_not_null(conn->username);
g_free_not_null(conn->password);
g_free_not_null(conn->nick);
g_free_not_null(conn->username);
g_free_not_null(conn->realname);
g_free_not_null(conn->ssl_cert);
g_free_not_null(conn->ssl_pkey);
g_free_not_null(conn->ssl_pass);
g_free_not_null(conn->ssl_cafile);
g_free_not_null(conn->ssl_capath);
g_free_not_null(conn->ssl_ciphers);
g_free_not_null(conn->tls_cert);
g_free_not_null(conn->tls_pkey);
g_free_not_null(conn->tls_pass);
g_free_not_null(conn->tls_cafile);
g_free_not_null(conn->tls_capath);
g_free_not_null(conn->tls_ciphers);
g_free_not_null(conn->tls_pinned_cert);
g_free_not_null(conn->tls_pinned_pubkey);
g_free_not_null(conn->channels);
g_free_not_null(conn->away_reason);
g_free_not_null(conn->away_reason);
conn->type = 0;
conn->type = 0;
g_free(conn);
}

View File

@ -150,8 +150,7 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
config_node_set_str(config, node, "chat_type",
chat_protocol_find_id(server->chat_type)->name);
config_node_set_str(config, node, "chat_type", chat_protocol_find_id(server->chat_type)->name);
config_node_set_str(config, node, "address", server->connrec->address);
config_node_set_int(config, node, "port", server->connrec->port);
config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
@ -159,13 +158,15 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "nick", server->nick);
config_node_set_str(config, node, "version", server->version);
config_node_set_bool(config, node, "use_ssl", server->connrec->use_ssl);
config_node_set_str(config, node, "ssl_cert", server->connrec->ssl_cert);
config_node_set_str(config, node, "ssl_pkey", server->connrec->ssl_pkey);
config_node_set_bool(config, node, "ssl_verify", server->connrec->ssl_verify);
config_node_set_str(config, node, "ssl_cafile", server->connrec->ssl_cafile);
config_node_set_str(config, node, "ssl_capath", server->connrec->ssl_capath);
config_node_set_str(config, node, "ssl_ciphers", server->connrec->ssl_ciphers);
config_node_set_bool(config, node, "use_tls", server->connrec->use_tls);
config_node_set_str(config, node, "tls_cert", server->connrec->tls_cert);
config_node_set_str(config, node, "tls_pkey", server->connrec->tls_pkey);
config_node_set_bool(config, node, "tls_verify", server->connrec->tls_verify);
config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile);
config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath);
config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers);
config_node_set_str(config, node, "tls_pinned_cert", server->connrec->tls_pinned_cert);
config_node_set_str(config, node, "tls_pinned_pubkey", server->connrec->tls_pinned_pubkey);
handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
config_node_set_int(config, node, "handle", handle);

214
src/core/tls.c Normal file
View File

@ -0,0 +1,214 @@
/*
* Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/
#include "module.h"
#include "tls.h"
TLS_REC *tls_create_rec()
{
TLS_REC *rec = g_new0(TLS_REC, 1);
g_return_val_if_fail(rec != NULL, NULL);
return rec;
}
void tls_rec_free(TLS_REC *tls_rec)
{
if (tls_rec == NULL)
return;
g_free_and_null(tls_rec->protocol_version);
g_free_and_null(tls_rec->cipher);
g_free_and_null(tls_rec->public_key_algorithm);
g_free_and_null(tls_rec->public_key_fingerprint);
g_free_and_null(tls_rec->public_key_fingerprint_algorithm);
g_free_and_null(tls_rec->certificate_fingerprint);
g_free_and_null(tls_rec->certificate_fingerprint_algorithm);
g_free_and_null(tls_rec->not_after);
g_free_and_null(tls_rec->not_before);
g_free_and_null(tls_rec->ephemeral_key_algorithm);
if (tls_rec->certs != NULL) {
g_slist_foreach(tls_rec->certs, (GFunc)tls_cert_rec_free, NULL);
g_slist_free(tls_rec->certs);
tls_rec->certs = NULL;
}
g_free(tls_rec);
}
void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->protocol_version = g_strdup(protocol_version);
}
void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->cipher = g_strdup(cipher);
}
void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->cipher_size = size;
}
void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->public_key_algorithm = g_strdup(algorithm);
}
void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->public_key_fingerprint = g_strdup(fingerprint);
}
void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->public_key_fingerprint_algorithm = g_strdup(algorithm);
}
void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->public_key_size = size;
}
void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->certificate_fingerprint = g_strdup(fingerprint);
}
void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->certificate_fingerprint_algorithm = g_strdup(algorithm);
}
void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->not_after = g_strdup(not_after);
}
void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->not_before = g_strdup(not_before);
}
void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->ephemeral_key_algorithm = g_strdup(algorithm);
}
void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size)
{
g_return_if_fail(tls_rec != NULL);
tls_rec->ephemeral_key_size = size;
}
void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec)
{
g_return_if_fail(tls_rec != NULL);
g_return_if_fail(tls_cert_rec != NULL);
tls_rec->certs = g_slist_append(tls_rec->certs, tls_cert_rec);
}
TLS_CERT_REC *tls_cert_create_rec()
{
TLS_CERT_REC *rec = g_new0(TLS_CERT_REC, 1);
g_return_val_if_fail(rec != NULL, NULL);
return rec;
}
void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec)
{
g_return_if_fail(tls_cert_rec != NULL);
g_return_if_fail(tls_cert_entry_rec != NULL);
tls_cert_rec->subject = g_slist_append(tls_cert_rec->subject, tls_cert_entry_rec);
}
void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec)
{
g_return_if_fail(tls_cert_rec != NULL);
g_return_if_fail(tls_cert_entry_rec != NULL);
tls_cert_rec->issuer = g_slist_append(tls_cert_rec->issuer, tls_cert_entry_rec);
}
void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec)
{
if (tls_cert_rec == NULL)
return;
if (tls_cert_rec->subject != NULL) {
g_slist_foreach(tls_cert_rec->subject, (GFunc)tls_cert_entry_rec_free, NULL);
g_slist_free(tls_cert_rec->subject);
tls_cert_rec->subject = NULL;
}
if (tls_cert_rec->issuer != NULL) {
g_slist_foreach(tls_cert_rec->issuer, (GFunc)tls_cert_entry_rec_free, NULL);
g_slist_free(tls_cert_rec->issuer);
tls_cert_rec->issuer = NULL;
}
g_free(tls_cert_rec);
}
TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value)
{
TLS_CERT_ENTRY_REC *rec = g_new0(TLS_CERT_ENTRY_REC, 1);
g_return_val_if_fail(rec != NULL, NULL);
rec->name = g_strdup(name);
rec->value = g_strdup(value);
return rec;
}
void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry)
{
if (tls_cert_entry == NULL)
return;
g_free_and_null(tls_cert_entry->name);
g_free_and_null(tls_cert_entry->value);
g_free(tls_cert_entry);
}

90
src/core/tls.h Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/
#ifndef __TLS_H
#define __TLS_H
#include <openssl/ssl.h>
#include <stdbool.h>
typedef struct _TLS_REC TLS_REC;
typedef struct _TLS_CERT_REC TLS_CERT_REC;
typedef struct _TLS_CERT_ENTRY_REC TLS_CERT_ENTRY_REC;
struct _TLS_REC {
char *protocol_version;
char *cipher;
size_t cipher_size;
char *public_key_algorithm;
char *public_key_fingerprint;
char *public_key_fingerprint_algorithm;
size_t public_key_size;
char *certificate_fingerprint;
char *certificate_fingerprint_algorithm;
char *not_after;
char *not_before;
char *ephemeral_key_algorithm;
size_t ephemeral_key_size;
GSList *certs;
};
struct _TLS_CERT_REC {
GSList *subject;
GSList *issuer;
};
struct _TLS_CERT_ENTRY_REC {
char *name;
char *value;
};
TLS_REC *tls_create_rec();
void tls_rec_free(TLS_REC *tls_rec);
void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version);
void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher);
void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size);
void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm);
void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint);
void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm);
void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size);
void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint);
void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm);
void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after);
void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before);
void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm);
void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size);
void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec);
TLS_CERT_REC *tls_cert_create_rec();
void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec);
void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec);
void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec);
TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value);
void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry);
#endif

View File

@ -24,6 +24,7 @@ libfe_common_core_a_SOURCES = \
fe-queries.c \
fe-server.c \
fe-settings.c \
fe-tls.c \
formats.c \
hilight-text.c \
keyboard.c \
@ -48,6 +49,7 @@ pkginc_fe_common_core_HEADERS = \
fe-exec.h \
fe-messages.h \
fe-queries.h \
fe-tls.h \
formats.h \
hilight-text.h \
keyboard.h \

View File

@ -88,6 +88,9 @@ void fe_server_deinit(void);
void fe_settings_init(void);
void fe_settings_deinit(void);
void fe_tls_init(void);
void fe_tls_deinit(void);
void window_commands_init(void);
void window_commands_deinit(void);
@ -176,6 +179,7 @@ void fe_common_core_init(void)
fe_modules_init();
fe_server_init();
fe_settings_init();
fe_tls_init();
windows_init();
window_activity_init();
window_commands_init();
@ -217,6 +221,7 @@ void fe_common_core_deinit(void)
fe_modules_deinit();
fe_server_deinit();
fe_settings_deinit();
fe_tls_deinit();
windows_deinit();
window_activity_deinit();
window_commands_deinit();

View File

@ -154,42 +154,66 @@ static void cmd_server_add_modify(const char *data, gboolean add)
else if (g_hash_table_lookup(optlist, "4"))
rec->family = AF_INET;
if (g_hash_table_lookup(optlist, "ssl"))
rec->use_ssl = TRUE;
if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl"))
rec->use_tls = TRUE;
value = g_hash_table_lookup(optlist, "ssl_cert");
value = g_hash_table_lookup(optlist, "tls_cert");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_cert");
if (value != NULL && *value != '\0')
rec->ssl_cert = g_strdup(value);
rec->tls_cert = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_pkey");
value = g_hash_table_lookup(optlist, "tls_pkey");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pkey");
if (value != NULL && *value != '\0')
rec->ssl_pkey = g_strdup(value);
rec->tls_pkey = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_pass");
value = g_hash_table_lookup(optlist, "tls_pass");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pass");
if (value != NULL && *value != '\0')
rec->ssl_pass = g_strdup(value);
rec->tls_pass = g_strdup(value);
if (g_hash_table_lookup(optlist, "ssl_verify"))
rec->ssl_verify = TRUE;
if (g_hash_table_lookup(optlist, "tls_verify") || g_hash_table_lookup(optlist, "ssl_verify"))
rec->tls_verify = TRUE;
value = g_hash_table_lookup(optlist, "ssl_cafile");
value = g_hash_table_lookup(optlist, "tls_cafile");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_cafile");
if (value != NULL && *value != '\0')
rec->ssl_cafile = g_strdup(value);
rec->tls_cafile = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_capath");
value = g_hash_table_lookup(optlist, "tls_capath");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_capath");
if (value != NULL && *value != '\0')
rec->ssl_capath = g_strdup(value);
rec->tls_capath = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_ciphers");
value = g_hash_table_lookup(optlist, "tls_ciphers");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_ciphers");
if (value != NULL && *value != '\0')
rec->ssl_ciphers = g_strdup(value);
rec->tls_ciphers = g_strdup(value);
if ((rec->ssl_cafile != NULL && rec->ssl_cafile[0] != '\0')
|| (rec->ssl_capath != NULL && rec->ssl_capath[0] != '\0'))
rec->ssl_verify = TRUE;
value = g_hash_table_lookup(optlist, "tls_pinned_cert");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pinned_cert");
if (value != NULL && *value != '\0')
rec->tls_pinned_cert = g_strdup(value);
if ((rec->ssl_cert != NULL && rec->ssl_cert[0] != '\0') || rec->ssl_verify == TRUE)
rec->use_ssl = TRUE;
value = g_hash_table_lookup(optlist, "tls_pinned_pubkey");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey");
if (value != NULL && *value != '\0')
rec->tls_pinned_pubkey = g_strdup(value);
if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0')
|| (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
rec->tls_verify = TRUE;
if ((rec->tls_cert != NULL && rec->tls_cert[0] != '\0') || rec->tls_verify == TRUE)
rec->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
@ -409,8 +433,9 @@ void fe_server_init(void)
command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/
#include "module.h"
#include "signals.h"
#include "settings.h"
#include "levels.h"
#include "tls.h"
#include "module-formats.h"
#include "printtext.h"
#include "fe-tls.h"
static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls)
{
GSList *certs = NULL;
GSList *subject = NULL;
GSList *issuer = NULL;
GString *str = NULL;
TLS_CERT_ENTRY_REC *data = NULL;
if (! settings_get_bool("tls_verbose_connect"))
return;
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER);
for (certs = tls->certs; certs != NULL; certs = certs->next) {
TLS_CERT_REC *tls_cert_rec = certs->data;
str = g_string_new(NULL);
for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) {
data = subject->data;
g_string_append_printf(str, "%s: %s, ", data->name, data->value);
}
if (str->len > 1)
g_string_truncate(str, str->len - 2);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT, str->str);
g_string_free(str, TRUE);
str = g_string_new(NULL);
for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) {
data = issuer->data;
g_string_append_printf(str, "%s: %s, ", data->name, data->value);
}
if (str->len > 1)
g_string_truncate(str, str->len - 2);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER, str->str);
g_string_free(str, TRUE);
}
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher);
if (tls->ephemeral_key_algorithm != NULL)
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm);
else
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_FINGERPRINT, tls->certificate_fingerprint, tls->certificate_fingerprint_algorithm);
}
void fe_tls_init(void)
{
settings_add_bool("lookandfeel", "tls_verbose_connect", TRUE);
signal_add("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
}
void fe_tls_deinit(void)
{
signal_remove("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/
#ifndef __FE_TLS_H
#define __FE_TLS_H
void fe_tls_init(void);
void fe_tls_deinit(void);
#endif

View File

@ -291,5 +291,18 @@ FORMAT_REC fecommon_core_formats[] = {
{ "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } },
{ "completion_footer", "", 0 },
/* ---- */
{ NULL, "TLS", 0 },
{ "tls_ephemeral_key", "EDH Key: {hilight $0} bit {hilight $1}", 2, { 1, 0 } },
{ "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 },
{ "tls_pubkey", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } },
{ "tls_cert_header", "Certificate Chain:", 0 },
{ "tls_cert_subject", " Subject: {hilight $0}", 1, { 0 } },
{ "tls_cert_issuer", " Issuer: {hilight $0}", 1, { 0 } },
{ "tls_pubkey_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
{ "tls_cert_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
{ "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } },
{ NULL, NULL, 0 }
};

View File

@ -254,7 +254,19 @@ enum {
TXT_COMPLETION_REMOVED,
TXT_COMPLETION_HEADER,
TXT_COMPLETION_LINE,
TXT_COMPLETION_FOOTER
TXT_COMPLETION_FOOTER,
TLS_FILL_15,
TXT_TLS_EPHEMERAL_KEY,
TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE,
TXT_TLS_PUBKEY,
TXT_TLS_CERT_HEADER,
TXT_TLS_CERT_SUBJECT,
TXT_TLS_CERT_ISSUER,
TXT_TLS_PUBKEY_FINGERPRINT,
TXT_TLS_CERT_FINGERPRINT,
TXT_TLS_PROTOCOL_VERSION
};
extern FORMAT_REC fecommon_core_formats[];

View File

@ -108,23 +108,27 @@ static void cmd_server_list(const char *data)
g_string_append(str, "autoconnect, ");
if (rec->no_proxy)
g_string_append(str, "noproxy, ");
if (rec->use_ssl) {
g_string_append(str, "ssl, ");
if (rec->ssl_cert) {
g_string_append_printf(str, "ssl_cert: %s, ", rec->ssl_cert);
if (rec->ssl_pkey)
g_string_append_printf(str, "ssl_pkey: %s, ", rec->ssl_pkey);
if (rec->ssl_pass)
if (rec->use_tls) {
g_string_append(str, "tls, ");
if (rec->tls_cert) {
g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert);
if (rec->tls_pkey)
g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey);
if (rec->tls_pass)
g_string_append_printf(str, "(pass), ");
}
if (rec->ssl_verify)
g_string_append(str, "ssl_verify, ");
if (rec->ssl_cafile)
g_string_append_printf(str, "ssl_cafile: %s, ", rec->ssl_cafile);
if (rec->ssl_capath)
g_string_append_printf(str, "ssl_capath: %s, ", rec->ssl_capath);
if (rec->ssl_ciphers)
g_string_append_printf(str, "ssl_ciphers: %s, ", rec->ssl_ciphers);
if (rec->tls_verify)
g_string_append(str, "tls_verify, ");
if (rec->tls_cafile)
g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile);
if (rec->tls_capath)
g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
if (rec->tls_ciphers)
g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
if (rec->tls_pinned_cert)
g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert);
if (rec->tls_pinned_pubkey)
g_string_append_printf(str, "tls_pinned_pubkey: %s, ", rec->tls_pinned_pubkey);
}
if (rec->max_cmds_at_once > 0)

View File

@ -310,7 +310,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
if (server->connrec->port <= 0) {
server->connrec->port =
server->connrec->use_ssl ? 6697 : 6667;
server->connrec->use_tls ? 6697 : 6667;
}
server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ?
@ -328,7 +328,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
ircconn->max_whois : DEFAULT_MAX_WHOIS;
server->max_msgs_in_cmd = ircconn->max_msgs > 0 ?
ircconn->max_msgs : DEFAULT_MAX_MSGS;
server->connrec->use_ssl = conn->use_ssl;
server->connrec->use_tls = conn->use_tls;
modes_server_init(server);

View File

@ -37,6 +37,7 @@ while (<STDIN>) {
RAWLOG_REC => 'Irssi::Rawlog',
IGNORE_REC => 'Irssi::Ignore',
MODULE_REC => 'Irssi::Module',
TLS_REC => 'iobject',
# irc
BAN_REC => 'Irssi::Irc::Ban',

View File

@ -301,7 +301,8 @@ void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
(void) hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0);
(void) hv_store(hv, "no_autosendcmd", 14, newSViv(conn->no_autosendcmd), 0);
(void) hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0);
(void) hv_store(hv, "use_ssl", 7, newSViv(conn->use_ssl), 0);
(void) hv_store(hv, "use_ssl", 7, newSViv(conn->use_tls), 0);
(void) hv_store(hv, "use_tls", 7, newSViv(conn->use_tls), 0);
(void) hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0);
}