Add SASL authentication support (EXTERNAL, PLAIN)
This commit is contained in:
parent
1df884545f
commit
dc43d75d1f
1
AUTHORS
1
AUTHORS
@ -10,3 +10,4 @@ Thanks to our marketting and management team:
|
|||||||
ack|, ato, blackmore, lafouine, Gaston & gromit
|
ack|, ato, blackmore, lafouine, Gaston & gromit
|
||||||
|
|
||||||
Crypto shamelessly stolen from Christophe 'sexy' Devine.
|
Crypto shamelessly stolen from Christophe 'sexy' Devine.
|
||||||
|
Credits to Jouni Malinen for base64 library (http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/)
|
||||||
|
16
bip.conf.5
16
bip.conf.5
@ -1,4 +1,4 @@
|
|||||||
.TH BIP.CONF 5 "17 October 2021"
|
.TH BIP.CONF 5 "2 January 2022"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
|
|
||||||
@ -402,6 +402,20 @@ BIP will send that string as the realname part (description in whois result)
|
|||||||
upon connect. If not specified and if \fBdefault_realname\fP is specified in
|
upon connect. If not specified and if \fBdefault_realname\fP is specified in
|
||||||
the \fBuser\fP section, BIP will use that default realname string.
|
the \fBuser\fP section, BIP will use that default realname string.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fBsasl_mechanism\fP
|
||||||
|
Tells BIP to use specified SASL mechanism. Currently supported: PLAIN, EXTERNAL.
|
||||||
|
PLAIN mechanism requires \fBsasl_username\fP and \fBsasl_password\fP and is the
|
||||||
|
default if these are set.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fBsasl_username\fP
|
||||||
|
This connection's username to pass on using SASL authentication.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fBsasl_password\fP
|
||||||
|
This connection's password to pass on using SASL authentication.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fBsource_port\fP
|
\fBsource_port\fP
|
||||||
If specified, tells BIP to connect from this port to the IRC server.
|
If specified, tells BIP to connect from this port to the IRC server.
|
||||||
|
@ -240,6 +240,11 @@
|
|||||||
#You can specify this field more than once. BIP will send the text as is to the server.
|
#You can specify this field more than once. BIP will send the text as is to the server.
|
||||||
#on_connect_send = "PRIVMSG NickServ :IDENTIFY nspassword";
|
#on_connect_send = "PRIVMSG NickServ :IDENTIFY nspassword";
|
||||||
|
|
||||||
|
# You can connect with SASL on networks supporting it
|
||||||
|
#sasl_username = "username";
|
||||||
|
#sasl_password = "sikioure password";
|
||||||
|
#sasl_mechanism = "PLAIN";
|
||||||
|
|
||||||
# Some options:
|
# Some options:
|
||||||
#away_nick = "bip`away";
|
#away_nick = "bip`away";
|
||||||
# Away message to be set when no client is connected
|
# Away message to be set when no client is connected
|
||||||
|
@ -91,7 +91,8 @@ syn keyword bipCoKeyword contained nextgroup=bipBoolV autojoin_on_kick
|
|||||||
\ follow_nick ignore_first_nick log ignore_server_capab
|
\ follow_nick ignore_first_nick log ignore_server_capab
|
||||||
syn keyword bipCoKeyword contained nextgroup=bipStringV name user nick
|
syn keyword bipCoKeyword contained nextgroup=bipStringV name user nick
|
||||||
\ network password vhost away_nick on_connect_send realname
|
\ network password vhost away_nick on_connect_send realname
|
||||||
\ no_client_away_msg ssl_check_mode
|
\ no_client_away_msg ssl_check_mode sasl_username sasl_password
|
||||||
|
\ sasl_mechanism
|
||||||
syn keyword bipCoKeyword contained nextgroup=bipNumericV source_port
|
syn keyword bipCoKeyword contained nextgroup=bipNumericV source_port
|
||||||
|
|
||||||
" Channel elements (lvl 2)
|
" Channel elements (lvl 2)
|
||||||
|
@ -10,7 +10,8 @@ libbip_a_SOURCES = conf.y lex.l \
|
|||||||
md5.c md5.h \
|
md5.c md5.h \
|
||||||
path_util.c path_util.h \
|
path_util.c path_util.h \
|
||||||
tuple.h \
|
tuple.h \
|
||||||
util.c util.h
|
util.c util.h \
|
||||||
|
utils/base64.c utils/base64.h
|
||||||
|
|
||||||
libbip_a_CFLAGS = ${OPENSSL_CFLAGS} $(AM_CFLAGS)
|
libbip_a_CFLAGS = ${OPENSSL_CFLAGS} $(AM_CFLAGS)
|
||||||
|
|
||||||
|
36
src/bip.c
36
src/bip.c
@ -2,7 +2,8 @@
|
|||||||
* $Id: bip.c,v 1.39 2005/04/21 06:58:50 nohar Exp $
|
* $Id: bip.c,v 1.39 2005/04/21 06:58:50 nohar Exp $
|
||||||
*
|
*
|
||||||
* This file is part of the bip project
|
* This file is part of the bip project
|
||||||
* Copyright (C) 2004 2005 Arnaud Cornet and Loïc Gomez
|
* Copyright (C) 2004,2005 Arnaud Cornet
|
||||||
|
* Copyright (C) 2004,2005,2022 Loïc Gomez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -480,6 +481,22 @@ static int add_connection(bip_t *bip, struct bipuser *user, list_t *data)
|
|||||||
case LEX_PASSWORD:
|
case LEX_PASSWORD:
|
||||||
MOVE_STRING(l->s_password, t->pdata);
|
MOVE_STRING(l->s_password, t->pdata);
|
||||||
break;
|
break;
|
||||||
|
case LEX_SASL_USERNAME:
|
||||||
|
MOVE_STRING(l->sasl_username, t->pdata);
|
||||||
|
break;
|
||||||
|
case LEX_SASL_PASSWORD:
|
||||||
|
MOVE_STRING(l->sasl_password, t->pdata);
|
||||||
|
break;
|
||||||
|
case LEX_SASL_MECHANISM:
|
||||||
|
if (strcmp(t->pdata, "PLAIN") == 0) {
|
||||||
|
l->sasl_mechanism = SASL_AUTH_PLAIN;
|
||||||
|
} else if (strcmp(t->pdata, "EXTERNAL") == 0) {
|
||||||
|
l->sasl_mechanism = SASL_AUTH_EXTERNAL;
|
||||||
|
} else {
|
||||||
|
conf_die(bip, "Unsupported SASL mechanism %s.", t->pdata);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case LEX_VHOST:
|
case LEX_VHOST:
|
||||||
MOVE_STRING(l->vhost, t->pdata);
|
MOVE_STRING(l->vhost, t->pdata);
|
||||||
break;
|
break;
|
||||||
@ -595,6 +612,23 @@ static int add_connection(bip_t *bip, struct bipuser *user, list_t *data)
|
|||||||
l->realname = bip_strdup(user->default_realname);
|
l->realname = bip_strdup(user->default_realname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (l->sasl_username && !l->sasl_password) {
|
||||||
|
conf_die(bip, "sasl_username set without sasl_password.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!l->sasl_username && l->sasl_password) {
|
||||||
|
conf_die(bip, "sasl_password set without sasl_username.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l->sasl_mechanism == SASL_AUTH_PLAIN && (!l->sasl_username || !l->sasl_password)) {
|
||||||
|
conf_die(bip, "SASL mechanism PLAIN requires username and password.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (l->sasl_username && !l->sasl_mechanism)
|
||||||
|
l->sasl_mechanism = SASL_AUTH_PLAIN;
|
||||||
|
|
||||||
l->in_use = 1;
|
l->in_use = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ struct tuple *tuple_l_new(int type, void *p)
|
|||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_DH_PARAM LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_TIMESTAMP LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_WRITE_OIDENTD LEX_OIDENTD_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER
|
%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_DH_PARAM LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_TIMESTAMP LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_WRITE_OIDENTD LEX_OIDENTD_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER LEX_SASL_USERNAME LEX_SASL_PASSWORD LEX_SASL_MECHANISM
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
int number;
|
int number;
|
||||||
@ -208,6 +208,12 @@ con_command:
|
|||||||
$3); }
|
$3); }
|
||||||
| LEX_PASSWORD LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PASSWORD,
|
| LEX_PASSWORD LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PASSWORD,
|
||||||
$3); }
|
$3); }
|
||||||
|
| LEX_SASL_USERNAME LEX_EQ LEX_STRING { $$ = tuple_s_new(
|
||||||
|
LEX_SASL_USERNAME, $3); }
|
||||||
|
| LEX_SASL_PASSWORD LEX_EQ LEX_STRING { $$ = tuple_s_new(
|
||||||
|
LEX_SASL_PASSWORD, $3); }
|
||||||
|
| LEX_SASL_MECHANISM LEX_EQ LEX_STRING { $$ = tuple_s_new(
|
||||||
|
LEX_SASL_MECHANISM, $3); }
|
||||||
| LEX_VHOST LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_VHOST, $3); }
|
| LEX_VHOST LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_VHOST, $3); }
|
||||||
| LEX_SOURCE_PORT LEX_EQ LEX_INT {
|
| LEX_SOURCE_PORT LEX_EQ LEX_INT {
|
||||||
$$ = tuple_i_new(LEX_SOURCE_PORT, $3); }
|
$$ = tuple_i_new(LEX_SOURCE_PORT, $3); }
|
||||||
|
177
src/irc.c
177
src/irc.c
@ -2,7 +2,8 @@
|
|||||||
* $Id: irc.c,v 1.156 2005/04/21 06:58:50 nohar Exp $
|
* $Id: irc.c,v 1.156 2005/04/21 06:58:50 nohar Exp $
|
||||||
*
|
*
|
||||||
* This file is part of the bip project
|
* This file is part of the bip project
|
||||||
* Copyright (C) 2004 2005 Arnaud Cornet and Loïc Gomez
|
* Copyright (C) 2004,2005 Arnaud Cornet
|
||||||
|
* Copyright (C) 2004,2005,2022 Loïc Gomez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -21,6 +22,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "md5.h"
|
#include "md5.h"
|
||||||
|
#include "utils/base64.h"
|
||||||
|
|
||||||
#define S_CONN_DELAY (10)
|
#define S_CONN_DELAY (10)
|
||||||
|
|
||||||
@ -67,6 +69,8 @@ static void irc_copy_cli(struct link_client *src, struct link_client *dest,
|
|||||||
static void irc_cli_make_join(struct link_client *ic);
|
static void irc_cli_make_join(struct link_client *ic);
|
||||||
static void server_setup_reconnect_timer(struct link *link);
|
static void server_setup_reconnect_timer(struct link *link);
|
||||||
int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line);
|
int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line);
|
||||||
|
static int irc_server_sasl_authenticate(struct link_server *ircs);
|
||||||
|
static char *sasl_mechanism_to_text(int sasl_mechanism);
|
||||||
|
|
||||||
#define LAGOUT_TIME 480
|
#define LAGOUT_TIME 480
|
||||||
#define LAGCHECK_TIME (90)
|
#define LAGCHECK_TIME (90)
|
||||||
@ -386,6 +390,92 @@ int irc_dispatch_server(bip_t *bip, struct link_server *server,
|
|||||||
}
|
}
|
||||||
ret = OK_FORGET;
|
ret = OK_FORGET;
|
||||||
}
|
}
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "CAP")) {
|
||||||
|
if (LINK(server)->sasl_mechanism) {
|
||||||
|
if (irc_line_elem_equals(line, 2, "ACK") && irc_line_elem_equals(line, 3, "sasl")) {
|
||||||
|
// Server is answering our CAP REQ :sasl and is SASL capable
|
||||||
|
char *sasl_mech = sasl_mechanism_to_text(LINK(server)->sasl_mechanism);
|
||||||
|
mylog(LOG_INFO, "[%s] Server is SASL capable, starting %s authentication.",
|
||||||
|
LINK(server)->name, sasl_mech);
|
||||||
|
WRITE_LINE1(CONN(server), NULL, "AUTHENTICATE", sasl_mech);
|
||||||
|
ret = OK_FORGET;
|
||||||
|
} else if (irc_line_elem_equals(line, 2, "NAK") && irc_line_elem_equals(line, 3, "sasl")) {
|
||||||
|
// Server is answering our CAP REQ :sasl and isn't SASL capable
|
||||||
|
mylog(LOG_INFO, "[%s] Server is not SASL capable.", LINK(server)->name);
|
||||||
|
ret = ERR_PROTOCOL;
|
||||||
|
} else {
|
||||||
|
// Unhandled CAP message
|
||||||
|
mylog(LOG_ERROR, "[%s] Unhandled CAP message: %s",
|
||||||
|
LINK(server)->name, irc_line_to_string(line));
|
||||||
|
ret = OK_FORGET;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unhandled CAP message
|
||||||
|
mylog(LOG_ERROR, "[%s] Unhandled CAP message: %s", LINK(server)->name,
|
||||||
|
irc_line_to_string(line));
|
||||||
|
ret = OK_FORGET;
|
||||||
|
}
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "AUTHENTICATE")) {
|
||||||
|
if (LINK(server)->sasl_mechanism) {
|
||||||
|
if (irc_line_count(line) == 2 && irc_line_elem_equals(line, 1, "+")) {
|
||||||
|
// Server is waiting for us to authenticate, let's do it
|
||||||
|
mylog(LOG_INFO, "[%s] Server accepted our authentication mechanism.",
|
||||||
|
LINK(server)->name);
|
||||||
|
ret = irc_server_sasl_authenticate(server);
|
||||||
|
} else {
|
||||||
|
// Anything else than "AUTHENTICATE +" is unknown to us
|
||||||
|
mylog(LOG_ERROR, "[%s] Server sent gibberish: %s",
|
||||||
|
LINK(server)->name, irc_line_to_string(line));
|
||||||
|
ret = ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unhandled AUTHENTICATE message
|
||||||
|
mylog(LOG_ERROR, "[%s] Unhandled AUTHENTICATE message: %s",
|
||||||
|
LINK(server)->name, irc_line_to_string(line));
|
||||||
|
ret = OK_FORGET;
|
||||||
|
}
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "900")) {
|
||||||
|
if (irc_line_count(line) >= 5) {
|
||||||
|
mylog(LOG_INFO, "[%s] Logged in as %s(%s): %s", LINK(server)->name,
|
||||||
|
irc_line_elem(line, 3), irc_line_elem(line, 2), irc_line_elem(line, 4));
|
||||||
|
} else {
|
||||||
|
mylog(LOG_INFO, "[%s] Logged in: %s", LINK(server)->name, irc_line_to_string(line));
|
||||||
|
}
|
||||||
|
ret = OK_FORGET;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "901")) {
|
||||||
|
if (irc_line_count(line) >= 4) {
|
||||||
|
mylog(LOG_INFO, "[%s] Logged out: %s",
|
||||||
|
LINK(server)->name, irc_line_elem(line, 3));
|
||||||
|
} else {
|
||||||
|
mylog(LOG_INFO, "[%s] Logged out: %s", LINK(server)->name, irc_line_to_string(line));
|
||||||
|
}
|
||||||
|
ret = OK_FORGET;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "902")) {
|
||||||
|
mylog(LOG_INFO, "[%s] Account unavailable: %s",
|
||||||
|
LINK(server)->name, irc_line_to_string(line));
|
||||||
|
ret = OK_FORGET;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "903")) {
|
||||||
|
mylog(LOG_INFO, "[%s] SASL authentication successful", LINK(server)->name);
|
||||||
|
WRITE_LINE1(CONN(server), NULL, "CAP", "END");
|
||||||
|
ret = OK_FORGET;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "904")) {
|
||||||
|
mylog(LOG_ERROR, "[%s] SASL authentication failed", LINK(server)->name);
|
||||||
|
ret = ERR_AUTH;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "905")) {
|
||||||
|
mylog(LOG_ERROR, "[%s] SASL message too long", LINK(server)->name);
|
||||||
|
ret = ERR_AUTH;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "906")) {
|
||||||
|
mylog(LOG_ERROR, "[%s] SASL authentication aborted by client",
|
||||||
|
LINK(server)->name);
|
||||||
|
ret = ERR_AUTH;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "907")) {
|
||||||
|
mylog(LOG_ERROR, "[%s] SASL authentication has already been completed",
|
||||||
|
LINK(server)->name);
|
||||||
|
ret = OK_FORGET;
|
||||||
|
} else if (irc_line_elem_equals(line, 0, "908")) {
|
||||||
|
mylog(LOG_ERROR, "[%s] Server only accepts following authentication mechanisms: %s",
|
||||||
|
LINK(server)->name, irc_line_elem(line, 2));
|
||||||
|
ret = ERR_AUTH;
|
||||||
} else if (irc_line_elem_equals(line, 0, "433")) {
|
} else if (irc_line_elem_equals(line, 0, "433")) {
|
||||||
if (LINK(server)->s_state != IRCS_CONNECTED) {
|
if (LINK(server)->s_state != IRCS_CONNECTED) {
|
||||||
size_t nicklen = strlen(server->nick);
|
size_t nicklen = strlen(server->nick);
|
||||||
@ -1927,6 +2017,83 @@ fake:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *sasl_mechanism_to_text(int sasl_mechanism)
|
||||||
|
{
|
||||||
|
switch (sasl_mechanism) {
|
||||||
|
case SASL_AUTH_EXTERNAL:
|
||||||
|
return "EXTERNAL";
|
||||||
|
case SASL_AUTH_PLAIN:
|
||||||
|
return "PLAIN";
|
||||||
|
default:
|
||||||
|
return "UNKOWN_MECHANISM";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per RFC send packets of max 400 chars at a time
|
||||||
|
#define SASL_AUTH_CHUNK_SZ 400
|
||||||
|
static int irc_server_sasl_authenticate(struct link_server *ircs)
|
||||||
|
{
|
||||||
|
char *sasl_username = LINK(ircs)->sasl_username;
|
||||||
|
char *sasl_password = LINK(ircs)->sasl_password;
|
||||||
|
|
||||||
|
if (LINK(ircs)->sasl_mechanism == SASL_AUTH_EXTERNAL) {
|
||||||
|
WRITE_LINE1(CONN(ircs), NULL, "AUTHENTICATE", "+");
|
||||||
|
return OK_FORGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not happen, but we never know right ?
|
||||||
|
if (!sasl_username || !sasl_password) {
|
||||||
|
mylog(LOG_ERROR, "[%s] Missing SASL username or password.", LINK(ircs)->name);
|
||||||
|
return ERR_AUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Other than EXTERNAL we only support PLAIN.
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t chunk_chars = SASL_AUTH_CHUNK_SZ;
|
||||||
|
char chunk[SASL_AUTH_CHUNK_SZ + 1];
|
||||||
|
size_t u_len = strlen(sasl_username);
|
||||||
|
size_t p_len = strlen(sasl_password);
|
||||||
|
size_t raw_len = u_len*2 + p_len + 2;
|
||||||
|
size_t enc_len;
|
||||||
|
unsigned char *raw_str = bip_malloc(raw_len + 1);
|
||||||
|
unsigned char *enc_str;
|
||||||
|
|
||||||
|
memcpy(raw_str, sasl_username, u_len);
|
||||||
|
raw_str[u_len] = '\0';
|
||||||
|
memcpy(raw_str + u_len + 1, sasl_username, u_len);
|
||||||
|
raw_str[u_len*2 + 1] = '\0';
|
||||||
|
memcpy(raw_str + u_len*2 + 2, sasl_password, p_len);
|
||||||
|
enc_str = base64_encode(raw_str, raw_len, &enc_len);
|
||||||
|
mylog(LOG_DEBUG, "[%s] Base64 encoded SASL auth token (len %d): %s", LINK(ircs)->name, enc_len, enc_str);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < enc_len; i += chunk_chars) {
|
||||||
|
size_t remaining = enc_len - i;
|
||||||
|
if (remaining < chunk_chars) {
|
||||||
|
memcpy(chunk, &enc_str[i], remaining);
|
||||||
|
chunk[remaining]= '\0';
|
||||||
|
} else {
|
||||||
|
memcpy(chunk, &enc_str[i], chunk_chars);
|
||||||
|
chunk[chunk_chars]= '\0';
|
||||||
|
}
|
||||||
|
mylog(LOG_DEBUG, "[%s] SASL AUTHENTICATE chunk %d, len %d: %s",
|
||||||
|
LINK(ircs)->name, i/chunk_chars, strlen(chunk), chunk);
|
||||||
|
WRITE_LINE1(CONN(ircs), NULL, "AUTHENTICATE", chunk);
|
||||||
|
|
||||||
|
// Send a closing AUTHENTICATE line if last chunk size was exactly 400
|
||||||
|
if (remaining == chunk_chars) {
|
||||||
|
mylog(LOG_DEBUG, "[%s] Last SASL chunk was exactly 400, sending +",
|
||||||
|
LINK(ircs)->name);
|
||||||
|
WRITE_LINE1(CONN(ircs), NULL, "AUTHENTICATE", "+");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(enc_str);
|
||||||
|
|
||||||
|
return OK_FORGET;
|
||||||
|
}
|
||||||
|
|
||||||
static void irc_server_startup(struct link_server *ircs)
|
static void irc_server_startup(struct link_server *ircs)
|
||||||
{
|
{
|
||||||
char *nick;
|
char *nick;
|
||||||
@ -2195,6 +2362,12 @@ connection_t *irc_server_connect(struct link *link)
|
|||||||
|
|
||||||
list_add_last(&_bip->conn_list, conn);
|
list_add_last(&_bip->conn_list, conn);
|
||||||
oidentd_dump(_bip);
|
oidentd_dump(_bip);
|
||||||
|
|
||||||
|
if (link->sasl_mechanism) {
|
||||||
|
mylog(LOG_INFO, "[%s] SASL (%s) enabled, sending CAP REQ.",
|
||||||
|
link->name, sasl_mechanism_to_text(link->sasl_mechanism));
|
||||||
|
WRITE_LINE2(conn, NULL, "CAP", "REQ", ":sasl");
|
||||||
|
}
|
||||||
irc_server_startup(ls);
|
irc_server_startup(ls);
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
@ -2635,6 +2808,8 @@ void link_kill(bip_t *bip, struct link *link)
|
|||||||
MAYFREE(link->username);
|
MAYFREE(link->username);
|
||||||
MAYFREE(link->realname);
|
MAYFREE(link->realname);
|
||||||
MAYFREE(link->s_password);
|
MAYFREE(link->s_password);
|
||||||
|
MAYFREE(link->sasl_username);
|
||||||
|
MAYFREE(link->sasl_password);
|
||||||
MAYFREE(link->connect_nick);
|
MAYFREE(link->connect_nick);
|
||||||
MAYFREE(link->vhost);
|
MAYFREE(link->vhost);
|
||||||
#ifdef HAVE_LIBSSL
|
#ifdef HAVE_LIBSSL
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
* $Id: irc.h,v 1.43 2005/04/21 06:58:50 nohar Exp $
|
* $Id: irc.h,v 1.43 2005/04/21 06:58:50 nohar Exp $
|
||||||
*
|
*
|
||||||
* This file is part of the bip project
|
* This file is part of the bip project
|
||||||
* Copyright (C) 2004 2005 Arnaud Cornet and Loïc Gomez
|
* Copyright (C) 2004,2005 Arnaud Cornet
|
||||||
|
* Copyright (C) 2004,2005,2022 Loïc Gomez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -106,6 +107,9 @@ struct network
|
|||||||
struct server *serverv;
|
struct server *serverv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SASL_AUTH_EXTERNAL 1
|
||||||
|
#define SASL_AUTH_PLAIN 2
|
||||||
|
|
||||||
struct link {
|
struct link {
|
||||||
char *name; /* id */
|
char *name; /* id */
|
||||||
|
|
||||||
@ -153,6 +157,9 @@ struct link {
|
|||||||
char *username;
|
char *username;
|
||||||
char *realname;
|
char *realname;
|
||||||
char *s_password;
|
char *s_password;
|
||||||
|
char *sasl_username;
|
||||||
|
char *sasl_password;
|
||||||
|
int sasl_mechanism;
|
||||||
char *connect_nick;
|
char *connect_nick;
|
||||||
|
|
||||||
/* socket creation info */
|
/* socket creation info */
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*
|
*
|
||||||
* This file is part of the bip proproject
|
* This file is part of the bip proproject
|
||||||
* Copyright (C) 2004 Arnaud Cornet
|
* Copyright (C) 2004 Arnaud Cornet
|
||||||
|
* Copyright (C) 2022 Loïc Gomez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -115,6 +116,9 @@ list_t *parse_conf(FILE *file, int *err)
|
|||||||
"client_side_ssl_pem" { return LEX_CSS_PEM; }
|
"client_side_ssl_pem" { return LEX_CSS_PEM; }
|
||||||
"client_side_ciphers" { return LEX_CSS_CIPHERS; }
|
"client_side_ciphers" { return LEX_CSS_CIPHERS; }
|
||||||
"client_side_dh_param" { return LEX_DH_PARAM; }
|
"client_side_dh_param" { return LEX_DH_PARAM; }
|
||||||
|
"sasl_username" { return LEX_SASL_USERNAME; }
|
||||||
|
"sasl_password" { return LEX_SASL_PASSWORD; }
|
||||||
|
"sasl_mechanism" { return LEX_SASL_MECHANISM; }
|
||||||
"ignore_server_capab" { return LEX_IGNORE_CAPAB; }
|
"ignore_server_capab" { return LEX_IGNORE_CAPAB; }
|
||||||
"reconn_timer" { return LEX_RECONN_TIMER; }
|
"reconn_timer" { return LEX_RECONN_TIMER; }
|
||||||
\"[^"]*\" {
|
\"[^"]*\" {
|
||||||
|
94
src/utils/base64.c
Normal file
94
src/utils/base64.c
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Base64 encoding/decoding (RFC1341)
|
||||||
|
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2022 Loïc Gomez
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
static const unsigned char base64_table[65] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64_encode - Base64 encode
|
||||||
|
* @src: Data to be encoded
|
||||||
|
* @len: Length of the data to be encoded
|
||||||
|
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||||
|
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||||
|
* or %NULL on failure
|
||||||
|
*
|
||||||
|
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||||
|
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||||
|
* not included in out_len.
|
||||||
|
*
|
||||||
|
* BIP change: remove line returns.
|
||||||
|
*/
|
||||||
|
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
unsigned char *out, *pos;
|
||||||
|
const unsigned char *end, *in;
|
||||||
|
size_t olen;
|
||||||
|
int line_len;
|
||||||
|
|
||||||
|
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||||
|
olen += olen / 72; /* line feeds */
|
||||||
|
olen++; /* nul termination */
|
||||||
|
if (olen < len)
|
||||||
|
return NULL; /* integer overflow */
|
||||||
|
out = malloc(olen);
|
||||||
|
if (out == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
end = src + len;
|
||||||
|
in = src;
|
||||||
|
pos = out;
|
||||||
|
line_len = 0;
|
||||||
|
while (end - in >= 3) {
|
||||||
|
*pos++ = base64_table[in[0] >> 2];
|
||||||
|
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||||
|
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||||
|
*pos++ = base64_table[in[2] & 0x3f];
|
||||||
|
in += 3;
|
||||||
|
line_len += 4;
|
||||||
|
/*
|
||||||
|
* BIP change: remove line returns.
|
||||||
|
if (line_len >= 72) {
|
||||||
|
*pos++ = '\n';
|
||||||
|
line_len = 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end - in) {
|
||||||
|
*pos++ = base64_table[in[0] >> 2];
|
||||||
|
if (end - in == 1) {
|
||||||
|
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||||
|
*pos++ = '=';
|
||||||
|
} else {
|
||||||
|
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||||
|
(in[1] >> 4)];
|
||||||
|
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||||
|
}
|
||||||
|
*pos++ = '=';
|
||||||
|
line_len += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BIP change: remove line returns.
|
||||||
|
if (line_len)
|
||||||
|
*pos++ = '\n';
|
||||||
|
*/
|
||||||
|
|
||||||
|
*pos = '\0';
|
||||||
|
if (out_len)
|
||||||
|
*out_len = (size_t)(pos - out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
16
src/utils/base64.h
Normal file
16
src/utils/base64.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Base64 encoding/decoding (RFC1341)
|
||||||
|
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2022 Loïc Gomez
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE64_H
|
||||||
|
#define BASE64_H
|
||||||
|
|
||||||
|
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||||
|
size_t *out_len);
|
||||||
|
|
||||||
|
#endif /* BASE64_H */
|
Loading…
Reference in New Issue
Block a user