1
0
mirror of https://github.com/irssi/irssi.git synced 2024-06-16 06:25:24 +00:00

Compare commits

...

25 Commits

Author SHA1 Message Date
Emil Engler
c019131471
Merge e578331192 into 5c15937554 2024-05-12 07:43:55 +05:00
ailin-nemui
5c15937554
Merge pull request #1525 from ailin-nemui/bans
fix /ban command
2024-04-12 21:18:39 +00:00
Ailin Nemui
19e7d3f6b8 up abi 2024-04-12 23:00:52 +02:00
Ailin Nemui
822fd50132 fix /ban command 2024-04-12 22:55:20 +02:00
ailin-nemui
4b48cdce65
Merge pull request #1523 from irssi/from-codeberg
From codeberg
2024-04-02 06:29:49 +00:00
ailin-nemui
c906b769fd
Merge pull request #1522 from dwfreed/fix-missing-nl
Ensure all text files have newlines at the end
2024-04-02 06:27:24 +00:00
Doug Freed
9d0787fc10 Ensure all files have newlines at the end 2024-04-02 05:23:10 +00:00
ailin-nemui
89f3e1acf5
Merge pull request #1519 from ailin-nemui/openssl3
deprecated openssl3 function
2024-04-01 20:36:00 +00:00
ailin-nemui
7b56ffdc2e
Merge pull request #1518 from ailin-nemui/dylib
fix deprecation in new glib module_open
2024-04-01 20:35:05 +00:00
ailin-nemui
bb4ed2b14f
Merge pull request #1521 from ailin-nemui/nodejs
github actions nodejs churn
2024-04-01 20:33:21 +00:00
Ailin Nemui
455dcb18ec deprecated openssl3 function 2024-04-01 22:31:53 +02:00
Ailin Nemui
111e9160a9 github actions nodejs churn 2024-04-01 22:28:17 +02:00
ailin-nemui
d30653f7f9
Merge pull request #1520 from irssi/readme-perl-ver
Update minimum required Perl version in readme
2024-04-01 20:11:15 +00:00
ailin-nemui
c48354307e
Update minimum required Perl version in readme 2024-04-01 22:10:35 +02:00
Ailin Nemui
e13df83dc8 new code for g_module_open which might work with apple dylibs given new enough glib 2024-04-01 17:38:30 +02:00
ailin-nemui
b18832bf3b
Merge pull request #1500 from patrick-irc/scram
Added support for SCRAM-SHA-1, SCRAM-SHA-256 and SCRAM-SHA-512
2024-04-01 15:17:16 +00:00
Ailin Nemui
2f2fa029f9 up abi 2024-04-01 16:37:35 +02:00
Ailin Nemui
08bb648850 proper sasl mechanism variable initialisation 2024-04-01 16:33:57 +02:00
Ailin Nemui
98b391f62e minor cleanup 2024-04-01 16:33:36 +02:00
Patrick Okraku
f2b97631e1 Added support for SCRAM-SHA-1, SCRAM-SHA-256 and SCRAM-SHA-512 2024-04-01 15:12:01 +02:00
ailin-nemui
215be29035 Merge pull request 'Fix github issue #1504 - irssi switches to af_unix if network name contains /' (!6) from ticho/irssi:gh1504 into master
Reviewed-on: https://codeberg.org/irssi/irssi/pulls/6
2024-04-01 08:42:17 +00:00
Andrej Kacian
e295caa866 Fix github issue #1504 - irssi switches to af_unix if network name contains /
In addition to looking for a /, we also check if the network name is
known.
2024-04-01 08:42:17 +00:00
Emil Engler
e578331192
fe-text: include the real tputs(3) from term.h
This commit includes the real `tpus(3)` function from the appropriate
`term.h` header file, if found.

This commit is necessary to fix a compiler warning on Darwin.
2023-08-14 11:56:39 +02:00
Emil Engler
67d961676d
fe-text: remove const for better Darwin support
This commit removes the `const` qualifier from certain member fields,
as the Darwin version of several `<term.h>` related functions only
accept a `char *` rather than a `const char *`, thereby resulting in
dozens of compiler warnings.
2023-08-14 11:53:34 +02:00
Emil Engler
47b7f5164a
build: fix linking error on arm64 Darwin
Currently, it is not possible to build git master on Darwin, as seen in
several issues reported previously.

This commit fixes that issues, by initializing two variables to NULL.
I have no idea, why this solves the linking error, as to my knowledge,
global variables do not need to be initialized.
2023-08-14 11:22:50 +02:00
29 changed files with 601 additions and 120 deletions

View File

@ -37,7 +37,7 @@ jobs:
echo base abi : $base_abi
./base$prefix/bin/irssi --version
echo base_abi=$base_abi >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: base.inst
path: base
@ -71,7 +71,7 @@ jobs:
echo merge abi : $merge_abi
./merge$prefix/bin/irssi --version
echo merge_abi=$merge_abi >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: merge.inst
path: merge
@ -89,12 +89,12 @@ jobs:
run: |
sudo apt install abigail-tools
- name: fetch base build
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: base.inst
path: base
- name: fetch merge build
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: merge.inst
path: merge
@ -103,7 +103,7 @@ jobs:
abipkgdiff -l base merge >abipkgdiff.out && diff_ret=0 || diff_ret=$?
echo "diff_ret=$diff_ret" >> $GITHUB_ENV
cat abipkgdiff.out
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
path: abipkgdiff.out
- run: |

View File

@ -21,7 +21,7 @@ jobs:
- name: make dist
run: |
./utils/make-dist.sh
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
path: irssi-*.tar.gz
retention-days: 1
@ -51,7 +51,7 @@ jobs:
flags: meson-latest FAILURE-OK
steps:
- name: fetch dist
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: set PATH
run: |
echo "$HOME/.local/bin" >> $GITHUB_PATH

View File

@ -26,4 +26,4 @@ jobs:
if: failure()
with:
name: git-clang-format.diff
path: git-clang-format.diff
path: git-clang-format.diff

View File

@ -73,7 +73,7 @@ jobs:
- name: build irssi package
run: |
sudo ./scripts/run-docker.sh ./build-package.sh -I irssi-an
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: irssi-termux-pkg
path: output/irssi-an*.deb

View File

@ -17,4 +17,4 @@ jobs:
repo: 'irssi.github.io',
workflow_id: 'pages.yml',
ref: 'main'
})
})

View File

@ -41,7 +41,7 @@ ninja -C Build && sudo ninja -C Build install
- [glib-2.32](https://wiki.gnome.org/Projects/GLib) or greater
- [openssl](https://www.openssl.org/)
- [perl-5.6](https://www.perl.org/) or greater (for perl support)
- [perl-5.8](https://www.perl.org/) or greater (for perl support)
- terminfo or ncurses (for text frontend)
#### See the [INSTALL](INSTALL) file for details

View File

@ -162,4 +162,4 @@ server reconnections and irc network splits</li>
<li>placing channels and queries in windows</li>
<li>nick completion</li>
<li>printing infomation of some events</li>
</ul>
</ul>

View File

@ -78,4 +78,4 @@
<h3 id="q-how-to-pronounce-irssi">Q: How to pronounce Irssi?</h3>
<p>A: Check <a href="/assets/irssi.wav">here</a></p>
<p>A: Check <a href="/assets/irssi.wav">here</a></p>

View File

@ -36,8 +36,8 @@
-cmdmax: Specifies the maximum number of commands to perform before
starting the internal flood protection.
-sasl_mechanism Specifies the mechanism to use for the SASL authentication.
At the moment irssi only supports the 'plain' and the
'external' mechanisms.
Irssi supports: PLAIN, EXTERNAL, SCRAM-SHA-1, SCRAM-SHA-256
and SCRAM-SHA-512
Use '' to disable the authentication.
-sasl_username Specifies the username to use during the SASL authentication.
-sasl_password Specifies the password to use during the SASL authentication.

View File

@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
#define IRSSI_ABI_VERSION 52
#define IRSSI_ABI_VERSION 54
#define DEFAULT_SERVER_ADD_PORT 6667
#define DEFAULT_SERVER_ADD_TLS_PORT 6697

View File

@ -30,6 +30,7 @@
#include <irssi/src/core/servers-setup.h>
#include <irssi/src/core/servers-reconnect.h>
#include <irssi/src/core/channels.h>
#include <irssi/src/core/chatnets.h>
#include <irssi/src/core/queries.h>
#include <irssi/src/core/window-item-def.h>
#include <irssi/src/core/rawlog.h>
@ -91,7 +92,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
return NULL;
}
if (strchr(addr, '/') != NULL)
if (strchr(addr, '/') != NULL && chatnet_find(addr) == NULL)
conn->unix_socket = TRUE;
/* TLS options are handled in server_create_conn_opt ... -> server_setup_fill_optlist */

View File

@ -102,10 +102,41 @@ static char *module_get_sub(const char *name, const char *root)
return g_strdup(name);
}
static GModule *module_open(const char *name, int *found)
static GModule *module_open(const char *name)
{
struct stat statbuf;
GModule *module;
#if GLIB_CHECK_VERSION(2, 75, 0)
/* in this version of glib, g_module_open knows how to construct system-dependent module
file names, and g_module_build_path is deprecated. */
char *path;
if (g_path_is_absolute(name) || *name == '~' ||
(*name == '.' && name[1] == G_DIR_SEPARATOR))
path = g_strdup(name);
else {
/* first try from home dir */
path = g_strdup_printf("%s/modules/%s", get_irssi_dir(), name);
module = g_module_open(path, (GModuleFlags) 0);
g_free(path);
if (module != NULL) {
return module;
}
/* module not found from home dir, try global module dir */
path = g_strdup_printf("%s/%s", MODULEDIR, name);
}
module = g_module_open(path, (GModuleFlags) 0);
g_free(path);
return module;
#else /* GLib < 2.75.0 */
/* in this version of glib, we build the module path with g_module_build_path.
unfortunately, this is broken on Darwin when compiled with meson. */
struct stat statbuf;
char *path, *str;
if (g_path_is_absolute(name) || *name == '~' ||
@ -120,7 +151,6 @@ static GModule *module_open(const char *name, int *found)
if (stat(path, &statbuf) == 0) {
module = g_module_open(path, (GModuleFlags) 0);
g_free(path);
*found = TRUE;
return module;
}
@ -129,10 +159,11 @@ static GModule *module_open(const char *name, int *found)
path = g_module_build_path(MODULEDIR, name);
}
*found = stat(path, &statbuf) == 0;
module = g_module_open(path, (GModuleFlags) 0);
g_free(path);
return module;
#endif
}
static char *module_get_func(const char *rootmodule, const char *submodule,
@ -151,8 +182,7 @@ static char *module_get_func(const char *rootmodule, const char *submodule,
signal_emit("module error", 4, GINT_TO_POINTER(error), text, \
rootmodule, submodule)
/* Returns 1 if ok, 0 if error in module and
-1 if module wasn't found */
/* Returns 1 if ok, 0 if not */
static int module_load_name(const char *path, const char *rootmodule,
const char *submodule, int silent)
{
@ -166,15 +196,15 @@ static int module_load_name(const char *path, const char *rootmodule,
gpointer value1, value2 = NULL;
char *versionfunc, *initfunc, *deinitfunc;
int module_abi_version = 0;
int found;
int valid;
gmodule = module_open(path, &found);
gmodule = module_open(path);
if (gmodule == NULL) {
if (!silent || found) {
if (!silent) {
module_error(MODULE_ERROR_LOAD, g_module_error(),
rootmodule, submodule);
}
return found ? 0 : -1;
return 0;
}
/* get the module's irssi abi version and bail out on mismatch */
@ -201,12 +231,12 @@ static int module_load_name(const char *path, const char *rootmodule,
/* get the module's init() and deinit() functions */
initfunc = module_get_func(rootmodule, submodule, "init");
deinitfunc = module_get_func(rootmodule, submodule, "deinit");
found = g_module_symbol(gmodule, initfunc, &value1) &&
g_module_symbol(gmodule, deinitfunc, &value2);
valid = g_module_symbol(gmodule, initfunc, &value1) &&
g_module_symbol(gmodule, deinitfunc, &value2);
g_free(initfunc);
g_free(deinitfunc);
if (!found) {
if (!valid) {
module_error(MODULE_ERROR_INVALID, NULL,
rootmodule, submodule);
g_module_close(gmodule);
@ -310,7 +340,7 @@ static int module_load_full(const char *path, const char *rootmodule,
/* check if the given module exists.. */
try_prefixes = g_strcmp0(rootmodule, submodule) == 0;
status = module_load_name(path, rootmodule, submodule, try_prefixes);
if (status == -1 && try_prefixes) {
if (status <= 0 && try_prefixes) {
/* nope, try loading the module_core,
fe_module, etc. */
status = module_load_prefixes(path, rootmodule,

View File

@ -28,6 +28,7 @@
#include <irssi/src/core/tls.h>
#include <openssl/crypto.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
@ -751,44 +752,49 @@ static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl)
#ifdef SSL_get_server_tmp_key
/* Show ephemeral key information. */
EVP_PKEY *ephemeral_key = NULL;
/* OPENSSL_NO_EC is for solaris 11.3 (2016), github ticket #598 */
#ifndef OPENSSL_NO_EC
EC_KEY *ec_key = NULL;
#endif
char *ephemeral_key_algorithm = NULL;
char *cname = NULL;
int nid;
g_return_if_fail(tls != NULL);
g_return_if_fail(ssl != NULL);
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;
int keytype = EVP_PKEY_id(ephemeral_key);
switch (keytype) {
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;
/* OPENSSL_NO_EC is for solaris 11.3 (2016), github ticket #598 */
#ifndef OPENSSL_NO_EC
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);
case EVP_PKEY_EC: {
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
char cname[50];
EVP_PKEY_get_group_name(ephemeral_key, cname, sizeof(cname), NULL);
#else
EC_KEY *ec_key = NULL;
char *cname = NULL;
int nid;
tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm);
tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
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);
#endif
ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname);
g_free_and_null(ephemeral_key_algorithm);
break;
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;
}
#endif
default:
tls_rec_set_ephemeral_key_algorithm(tls, "Unknown");
tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
break;
default:
tls_rec_set_ephemeral_key_algorithm(tls, OBJ_nid2ln(keytype));
tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
break;
}
EVP_PKEY_free(ephemeral_key);

View File

@ -34,8 +34,8 @@
#include "default-theme.h"
GSList *themes;
THEME_REC *current_theme;
GHashTable *default_formats;
THEME_REC *current_theme = NULL;
GHashTable *default_formats = NULL;
static int init_finished;
static char *init_errors;

View File

@ -35,6 +35,7 @@
#include <irssi/src/fe-common/core/printtext.h>
#include <irssi/src/fe-common/core/fe-channels.h>
#include <irssi/src/fe-common/irc/fe-irc-server.h>
#include <irssi/src/fe-common/irc/fe-irc-channels.h>
static void print_event_received(IRC_SERVER_REC *server, const char *data,
const char *nick, int target_param);
@ -138,38 +139,6 @@ static void event_end_of_who(IRC_SERVER_REC *server, const char *data)
g_free(params);
}
/* Get time elapsed since an event */
static char *time_ago(time_t seconds)
{
static char ret[128];
long unsigned years, weeks, days, hours, minutes;
seconds = time(NULL) - seconds;
years = seconds / (86400 * 365);
seconds %= (86400 * 365);
weeks = seconds / 604800;
days = (seconds / 86400) % 7;
hours = (seconds / 3600) % 24;
minutes = (seconds / 60) % 60;
seconds %= 60;
if (years)
snprintf(ret, sizeof(ret), "%luy %luw %lud", years, weeks, days);
else if (weeks)
snprintf(ret, sizeof(ret), "%luw %lud %luh", weeks, days, hours);
else if (days)
snprintf(ret, sizeof(ret), "%lud %luh %lum", days, hours, minutes);
else if (hours)
snprintf(ret, sizeof(ret), "%luh %lum", hours, minutes);
else if (minutes)
snprintf(ret, sizeof(ret), "%lum %lus", minutes, (long unsigned) seconds);
else
snprintf(ret, sizeof(ret), "%lus", (long unsigned) seconds);
return ret;
}
static void event_ban_list(IRC_SERVER_REC *server, const char *data)
{
IRC_CHANNEL_REC *chanrec;

View File

@ -73,6 +73,38 @@ const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target)
return target;
}
/* Get time elapsed since an event */
char *time_ago(time_t seconds)
{
static char ret[128];
long unsigned years, weeks, days, hours, minutes;
seconds = time(NULL) - seconds;
years = seconds / (86400 * 365);
seconds %= (86400 * 365);
weeks = seconds / 604800;
days = (seconds / 86400) % 7;
hours = (seconds / 3600) % 24;
minutes = (seconds / 60) % 60;
seconds %= 60;
if (years)
snprintf(ret, sizeof(ret), "%luy %luw %lud", years, weeks, days);
else if (weeks)
snprintf(ret, sizeof(ret), "%luw %lud %luh", weeks, days, hours);
else if (days)
snprintf(ret, sizeof(ret), "%lud %luh %lum", days, hours, minutes);
else if (hours)
snprintf(ret, sizeof(ret), "%luh %lum", hours, minutes);
else if (minutes)
snprintf(ret, sizeof(ret), "%lum %lus", minutes, (long unsigned) seconds);
else
snprintf(ret, sizeof(ret), "%lus", (long unsigned) seconds);
return ret;
}
static void sig_channel_rejoin(SERVER_REC *server, REJOIN_REC *rec)
{
g_return_if_fail(rec != NULL);

View File

@ -3,6 +3,7 @@
int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target);
const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target);
char *time_ago(time_t seconds);
void fe_irc_channels_init(void);
void fe_irc_channels_deinit(void);

View File

@ -39,6 +39,7 @@
#include <irssi/src/fe-common/core/window-items.h>
#include <irssi/src/fe-common/core/printtext.h>
#include <irssi/src/fe-common/core/keyboard.h>
#include <irssi/src/fe-common/irc/fe-irc-channels.h>
/* SYNTAX: ME <message> */
static void cmd_me(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
@ -224,15 +225,16 @@ static void bans_show_channel(IRC_CHANNEL_REC *channel, IRC_SERVER_REC *server)
/* show bans.. */
counter = 1;
for (tmp = channel->banlist; tmp != NULL; tmp = tmp->next) {
char *timestr, *ago;
BAN_REC *rec = tmp->data;
timestr = my_asctime(rec->time);
ago = time_ago(rec->time);
printformat(server, channel->visible_name, MSGLEVEL_CRAP,
(rec->setby == NULL || *rec->setby == '\0') ?
IRCTXT_BANLIST : IRCTXT_BANLIST_LONG,
counter, channel->visible_name,
rec->ban, rec->setby,
(int) (time(NULL)-rec->time));
counter++;
(rec->setby == NULL || *rec->setby == '\0') ? IRCTXT_BANLIST :
IRCTXT_BANLIST_LONG,
counter, channel->visible_name, rec->ban, rec->setby, ago, timestr);
counter++;
}
}

View File

@ -35,8 +35,8 @@ int tigetflag();
#define CAP_TYPE_STR 2
typedef struct {
const char *ti_name; /* terminfo name */
const char *tc_name; /* termcap name */
char *ti_name; /* terminfo name */
char *tc_name; /* termcap name */
int type;
unsigned int offset;
} TERMINFO_REC;

View File

@ -56,32 +56,32 @@ struct _TERM_REC {
int width, height;
/* Cursor movement */
const char *TI_smcup, *TI_rmcup, *TI_cup;
const char *TI_hpa, *TI_vpa, *TI_cub1, *TI_cuf1;
const char *TI_civis, *TI_cnorm;
char *TI_smcup, *TI_rmcup, *TI_cup;
char *TI_hpa, *TI_vpa, *TI_cub1, *TI_cuf1;
char *TI_civis, *TI_cnorm;
/* Scrolling */
const char *TI_csr, *TI_wind;
const char *TI_ri, *TI_rin, *TI_ind, *TI_indn;
const char *TI_il, *TI_il1, *TI_dl, *TI_dl1;
char *TI_csr, *TI_wind;
char *TI_ri, *TI_rin, *TI_ind, *TI_indn;
char *TI_il, *TI_il1, *TI_dl, *TI_dl1;
/* Clearing screen */
const char *TI_clear, *TI_ed; /* + *TI_dl, *TI_dl1; */
char *TI_clear, *TI_ed; /* + *TI_dl, *TI_dl1; */
/* Clearing to end of line */
const char *TI_el;
char *TI_el;
/* Repeating character */
const char *TI_rep;
char *TI_rep;
/* Colors */
int TI_colors; /* numbers of colors in TI_fg[] and TI_bg[] */
const char *TI_sgr0; /* turn off all attributes */
const char *TI_smul, *TI_rmul; /* underline on/off */
const char *TI_smso, *TI_rmso; /* standout on/off */
const char *TI_sitm, *TI_ritm; /* italic on/off */
const char *TI_bold, *TI_blink, *TI_rev;
const char *TI_setaf, *TI_setab, *TI_setf, *TI_setb;
char *TI_sgr0; /* turn off all attributes */
char *TI_smul, *TI_rmul; /* underline on/off */
char *TI_smso, *TI_rmso; /* standout on/off */
char *TI_sitm, *TI_ritm; /* italic on/off */
char *TI_bold, *TI_blink, *TI_rev;
char *TI_setaf, *TI_setab, *TI_setf, *TI_setb;
/* Colors - generated and dynamically allocated */
char **TI_fg, **TI_bg, *TI_normal;
@ -90,8 +90,8 @@ struct _TERM_REC {
char *TI_bel;
/* Keyboard-transmit mode */
const char *TI_smkx;
const char *TI_rmkx;
char *TI_smkx;
char *TI_rmkx;
/* Terminal mode states */
int appkey_enabled;

View File

@ -77,6 +77,10 @@ static void destroy_server_connect(SERVER_CONNECT_REC *conn)
g_free_not_null(ircconn->alternate_nick);
g_free_not_null(ircconn->sasl_username);
g_free_not_null(ircconn->sasl_password);
if (ircconn->scram_session != NULL) {
scram_session_free(ircconn->scram_session);
}
}
void irc_core_init(void)

View File

@ -128,12 +128,31 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
conn->sasl_password = g_strdup(ircnet->sasl_password);
} else
g_warning("The fields sasl_username and sasl_password are either missing or empty");
}
else if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "external")) {
} else if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "SCRAM-SHA-1") ||
!g_ascii_strcasecmp(ircnet->sasl_mechanism, "SCRAM-SHA-256") ||
!g_ascii_strcasecmp(ircnet->sasl_mechanism, "SCRAM-SHA-512")) {
/* The SCRAM-SHA-* methods need both the username and the password */
if (ircnet->sasl_username != NULL && *ircnet->sasl_username &&
ircnet->sasl_password != NULL && *ircnet->sasl_password) {
if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "SCRAM-SHA-1"))
conn->sasl_mechanism = SASL_MECHANISM_SCRAM_SHA_1;
if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "SCRAM-SHA-256"))
conn->sasl_mechanism = SASL_MECHANISM_SCRAM_SHA_256;
if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "SCRAM-SHA-512"))
conn->sasl_mechanism = SASL_MECHANISM_SCRAM_SHA_512;
conn->sasl_username = g_strdup(ircnet->sasl_username);
conn->sasl_password = g_strdup(ircnet->sasl_password);
} else
g_warning("The fields sasl_username and sasl_password are either "
"missing or empty");
} else if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "external")) {
conn->sasl_mechanism = SASL_MECHANISM_EXTERNAL;
} else {
g_warning("Unsupported SASL mechanism \"%s\" selected",
ircnet->sasl_mechanism);
conn->sasl_mechanism = SASL_MECHANISM_MAX;
}
else
g_warning("Unsupported SASL mechanism \"%s\" selected", ircnet->sasl_mechanism);
}
}

View File

@ -475,6 +475,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
server->send_message = send_message;
server->query_find_func = (QUERY_REC * (*) (SERVER_REC *, const char *) ) irc_query_find;
server->nick_comp_func = irc_nickcmp_rfc1459;
server->sasl_success = FALSE;
server_connect_init((SERVER_REC *) server);
return (SERVER_REC *) server;

View File

@ -4,6 +4,7 @@
#include <irssi/src/core/chat-protocols.h>
#include <irssi/src/core/servers.h>
#include <irssi/src/irc/core/modes.h>
#include <irssi/src/irc/core/scram.h>
/*
* 63 is the maximum hostname length defined by the protocol. 10 is a common
@ -54,6 +55,7 @@ struct _IRC_SERVER_CONNECT_REC {
int sasl_mechanism;
char *sasl_username;
char *sasl_password;
SCRAM_SESSION_REC *scram_session;
int max_cmds_at_once;
int cmd_queue_speed;

View File

@ -28,6 +28,7 @@ libirc_core_a = static_library('irc_core',
'modes.c',
'netsplit.c',
'sasl.c',
'scram.c',
'servers-idle.c',
'servers-redirect.c',
),
@ -70,6 +71,7 @@ install_headers(
'module.h',
'netsplit.h',
'sasl.h',
'scram.h',
'servers-idle.h',
'servers-redirect.h',
),

View File

@ -80,6 +80,24 @@ static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *fro
case SASL_MECHANISM_EXTERNAL:
irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
break;
case SASL_MECHANISM_SCRAM_SHA_1:
irc_send_cmd_now(server, "AUTHENTICATE SCRAM-SHA-1");
break;
case SASL_MECHANISM_SCRAM_SHA_256:
irc_send_cmd_now(server, "AUTHENTICATE SCRAM-SHA-256");
break;
case SASL_MECHANISM_SCRAM_SHA_512:
irc_send_cmd_now(server, "AUTHENTICATE SCRAM-SHA-512");
break;
case SASL_MECHANISM_MAX:
signal_emit("server sasl failure", 2, server,
"Irssi: Unsupported SASL mechanism");
irc_cap_finish_negotiation(server);
return;
}
server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
}
@ -223,6 +241,54 @@ void sasl_send_response(IRC_SERVER_REC *server, GString *response)
g_free(enc);
}
/*
* Sends AUTHENTICATE messages to log in via SCRAM.
*/
static void scram_authenticate(IRC_SERVER_REC *server, const char *data, const char *digest)
{
char *output;
int ret;
size_t output_len;
IRC_SERVER_CONNECT_REC *conn = server->connrec;
if (conn->scram_session == NULL) {
conn->scram_session =
scram_session_create(digest, conn->sasl_username, conn->sasl_password);
if (conn->scram_session == NULL) {
g_critical("Could not create SCRAM session with digest %s", digest);
irc_send_cmd_now(server, "AUTHENTICATE *");
return;
}
}
ret = scram_process(conn->scram_session, data, &output, &output_len);
if (ret == SCRAM_IN_PROGRESS) {
// Authentication is still in progress
GString *resp = g_string_new_len(output, output_len);
sasl_send_response(server, resp);
g_string_free(resp, TRUE);
g_free(output);
} else if (ret == SCRAM_SUCCESS) {
// Authentication succeeded
sasl_send_response(server, NULL);
scram_session_free(conn->scram_session);
conn->scram_session = NULL;
} else if (ret == SCRAM_ERROR) {
// Authentication failed
irc_send_cmd_now(server, "AUTHENTICATE *");
if (conn->scram_session->error != NULL) {
g_warning("SASL SCRAM authentication failed: %s",
conn->scram_session->error);
}
scram_session_free(conn->scram_session);
conn->scram_session = NULL;
}
}
/*
* Called when the incoming SASL request is completely received.
*/
@ -258,6 +324,18 @@ static void sasl_step_complete(IRC_SERVER_REC *server, GString *data)
/* Empty response */
sasl_send_response(server, NULL);
break;
case SASL_MECHANISM_SCRAM_SHA_1:
scram_authenticate(server, data->str, "SHA1");
break;
case SASL_MECHANISM_SCRAM_SHA_256:
scram_authenticate(server, data->str, "SHA256");
break;
case SASL_MECHANISM_SCRAM_SHA_512:
scram_authenticate(server, data->str, "SHA512");
break;
}
}

View File

@ -25,6 +25,9 @@ enum {
SASL_MECHANISM_NONE = 0,
SASL_MECHANISM_PLAIN,
SASL_MECHANISM_EXTERNAL,
SASL_MECHANISM_SCRAM_SHA_1,
SASL_MECHANISM_SCRAM_SHA_256,
SASL_MECHANISM_SCRAM_SHA_512,
SASL_MECHANISM_MAX
};

304
src/irc/core/scram.c Normal file
View File

@ -0,0 +1,304 @@
/*
scram.c : irssi
Copyright (C) 2023 Patrick Okraku
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 <irssi/src/irc/core/scram.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#define NONCE_LENGTH 18
#define CLIENT_KEY "Client Key"
#define SERVER_KEY "Server Key"
// EVP_MD_CTX_create() and EVP_MD_CTX_destroy() were renamed in OpenSSL 1.1.0
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
#define EVP_MD_CTX_new(ctx) EVP_MD_CTX_create(ctx)
#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx)
#endif
SCRAM_SESSION_REC *scram_session_create(const char *digest, const char *username,
const char *password)
{
SCRAM_SESSION_REC *session;
const EVP_MD *md;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
OpenSSL_add_all_algorithms();
#endif
md = EVP_get_digestbyname(digest);
if (md == NULL) {
// Unknown message digest
return NULL;
}
session = g_new0(SCRAM_SESSION_REC, 1);
session->digest = md;
session->digest_size = EVP_MD_size(md);
session->username = g_strdup(username);
session->password = g_strdup(password);
return session;
}
void scram_session_free(SCRAM_SESSION_REC *session)
{
if (session == NULL) {
return;
}
g_free(session->username);
g_free(session->password);
g_free(session->client_nonce_b64);
g_free(session->client_first_message_bare);
g_free(session->salted_password);
g_free(session->auth_message);
g_free(session->error);
g_free(session);
}
static int create_nonce(void *buffer, size_t length)
{
return RAND_bytes(buffer, length);
}
static int create_SHA(SCRAM_SESSION_REC *session, const unsigned char *input, size_t input_len,
unsigned char *output, unsigned int *output_len)
{
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (!EVP_DigestInit_ex(md_ctx, session->digest, NULL)) {
session->error = g_strdup("Message digest initialization failed");
EVP_MD_CTX_free(md_ctx);
return SCRAM_ERROR;
}
if (!EVP_DigestUpdate(md_ctx, input, input_len)) {
session->error = g_strdup("Message digest update failed");
EVP_MD_CTX_free(md_ctx);
return SCRAM_ERROR;
}
if (!EVP_DigestFinal_ex(md_ctx, output, output_len)) {
session->error = g_strdup("Message digest finalization failed");
EVP_MD_CTX_free(md_ctx);
return SCRAM_ERROR;
}
EVP_MD_CTX_free(md_ctx);
return SCRAM_IN_PROGRESS;
}
static scram_status process_client_first(SCRAM_SESSION_REC *session, char **output,
size_t *output_len)
{
char nonce[NONCE_LENGTH];
if (!create_nonce(nonce, NONCE_LENGTH)) {
session->error = g_strdup("Could not create client nonce");
return SCRAM_ERROR;
}
session->client_nonce_b64 = g_base64_encode((guchar *) nonce, NONCE_LENGTH);
*output = g_strdup_printf("n,,n=%s,r=%s", session->username, session->client_nonce_b64);
*output_len = strlen(*output);
session->client_first_message_bare = g_strdup(*output + 3);
session->step++;
return SCRAM_IN_PROGRESS;
}
static scram_status process_server_first(SCRAM_SESSION_REC *session, const char *data,
char **output, size_t *output_len)
{
char **params, *client_final_message_without_proof, *salt, *server_nonce_b64,
*client_proof_b64;
unsigned char *client_key, stored_key[EVP_MAX_MD_SIZE], *client_signature, *client_proof;
unsigned int i, param_count, iteration_count, client_key_len, stored_key_len;
gsize salt_len = 0;
size_t client_nonce_len;
params = g_strsplit(data, ",", -1);
param_count = g_strv_length(params);
if (param_count < 3) {
/* Invalid server-first-message */
session->error = g_strdup_printf("%s", data);
g_strfreev(params);
return SCRAM_ERROR;
}
server_nonce_b64 = NULL;
salt = NULL;
iteration_count = 0;
for (i = 0; i < param_count; i++) {
if (!strncmp(params[i], "r=", 2)) {
g_free(server_nonce_b64);
server_nonce_b64 = g_strdup(params[i] + 2);
} else if (!strncmp(params[i], "s=", 2)) {
g_free(salt);
salt = g_strdup(params[i] + 2);
} else if (!strncmp(params[i], "i=", 2)) {
iteration_count = strtoul(params[i] + 2, NULL, 10);
}
}
g_strfreev(params);
if (server_nonce_b64 == NULL || *server_nonce_b64 == '\0' || salt == NULL ||
*salt == '\0' || iteration_count == 0) {
session->error = g_strdup_printf("Invalid server-first-message: %s", data);
g_free(server_nonce_b64);
g_free(salt);
return SCRAM_ERROR;
}
client_nonce_len = strlen(session->client_nonce_b64);
// The server can append his nonce to the client's nonce
if (strlen(server_nonce_b64) < client_nonce_len ||
strncmp(server_nonce_b64, session->client_nonce_b64, client_nonce_len)) {
session->error = g_strdup_printf("Invalid server nonce: %s", server_nonce_b64);
return SCRAM_ERROR;
}
g_base64_decode_inplace((gchar *) salt, &salt_len);
// SaltedPassword := Hi(Normalize(password), salt, i)
session->salted_password = g_malloc(session->digest_size);
PKCS5_PBKDF2_HMAC(session->password, strlen(session->password), (unsigned char *) salt,
salt_len, iteration_count, session->digest, session->digest_size,
session->salted_password);
// AuthMessage := client-first-message-bare + "," +
// server-first-message + "," +
// client-final-message-without-proof
client_final_message_without_proof = g_strdup_printf("c=biws,r=%s", server_nonce_b64);
session->auth_message = g_strdup_printf("%s,%s,%s", session->client_first_message_bare,
data, client_final_message_without_proof);
// ClientKey := HMAC(SaltedPassword, "Client Key")
client_key = g_malloc0(session->digest_size);
HMAC(session->digest, session->salted_password, session->digest_size,
(unsigned char *) CLIENT_KEY, strlen(CLIENT_KEY), client_key, &client_key_len);
// StoredKey := H(ClientKey)
if (!create_SHA(session, client_key, session->digest_size, stored_key, &stored_key_len)) {
g_free(client_final_message_without_proof);
g_free(server_nonce_b64);
g_free(salt);
g_free(client_key);
return SCRAM_ERROR;
}
// ClientSignature := HMAC(StoredKey, AuthMessage)
client_signature = g_malloc0(session->digest_size);
HMAC(session->digest, stored_key, stored_key_len, (unsigned char *) session->auth_message,
strlen((char *) session->auth_message), client_signature, NULL);
// ClientProof := ClientKey XOR ClientSignature
client_proof = g_malloc0(client_key_len);
for (i = 0; i < client_key_len; i++) {
client_proof[i] = client_key[i] ^ client_signature[i];
}
client_proof_b64 = g_base64_encode((guchar *) client_proof, client_key_len);
*output = g_strdup_printf("%s,p=%s", client_final_message_without_proof, client_proof_b64);
*output_len = strlen(*output);
g_free(server_nonce_b64);
g_free(salt);
g_free(client_final_message_without_proof);
g_free(client_key);
g_free(client_signature);
g_free(client_proof);
g_free(client_proof_b64);
session->step++;
return SCRAM_IN_PROGRESS;
}
static scram_status process_server_final(SCRAM_SESSION_REC *session, const char *data)
{
char *verifier;
unsigned char *server_key, *server_signature;
unsigned int server_key_len = 0, server_signature_len = 0;
gsize verifier_len = 0;
if (strlen(data) < 3 || (data[0] != 'v' && data[1] != '=')) {
return SCRAM_ERROR;
}
verifier = g_strdup(data + 2);
g_base64_decode_inplace(verifier, &verifier_len);
// ServerKey := HMAC(SaltedPassword, "Server Key")
server_key = g_malloc0(session->digest_size);
HMAC(session->digest, session->salted_password, session->digest_size,
(unsigned char *) SERVER_KEY, strlen(SERVER_KEY), server_key, &server_key_len);
// ServerSignature := HMAC(ServerKey, AuthMessage)
server_signature = g_malloc0(session->digest_size);
HMAC(session->digest, server_key, session->digest_size,
(unsigned char *) session->auth_message, strlen((char *) session->auth_message),
server_signature, &server_signature_len);
if (verifier_len == server_signature_len &&
memcmp(verifier, server_signature, verifier_len) == 0) {
g_free(verifier);
g_free(server_key);
g_free(server_signature);
return SCRAM_SUCCESS;
} else {
g_free(verifier);
g_free(server_key);
g_free(server_signature);
return SCRAM_ERROR;
}
}
scram_status scram_process(SCRAM_SESSION_REC *session, const char *input, char **output,
size_t *output_len)
{
scram_status status;
switch (session->step) {
case 0:
status = process_client_first(session, output, output_len);
break;
case 1:
status = process_server_first(session, input, output, output_len);
break;
case 2:
status = process_server_final(session, input);
break;
default:
*output = NULL;
*output_len = 0;
status = SCRAM_ERROR;
break;
}
return status;
}

27
src/irc/core/scram.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef IRSSI_IRC_CORE_SCRAM_H
#define IRSSI_IRC_CORE_SCRAM_H
#include <openssl/evp.h>
typedef struct {
const EVP_MD *digest;
size_t digest_size;
char *username;
char *password;
char *client_nonce_b64;
char *client_first_message_bare;
unsigned char *salted_password;
char *auth_message;
char *error;
int step;
} SCRAM_SESSION_REC;
typedef enum { SCRAM_ERROR = 0, SCRAM_IN_PROGRESS, SCRAM_SUCCESS } scram_status;
SCRAM_SESSION_REC *scram_session_create(const char *digset, const char *username,
const char *password);
void scram_session_free(SCRAM_SESSION_REC *session);
scram_status scram_process(SCRAM_SESSION_REC *session, const char *input, char **output,
size_t *output_len);
#endif