From 0786e6016e05802332c9badb3c88e55a5b3cd119 Mon Sep 17 00:00:00 2001 From: Jan Henning Thorsen Date: Fri, 8 Jan 2021 08:58:20 +0900 Subject: [PATCH] Fix SASL authentication process for Oragono IRCd #356 How to test: 1. Connect without SASL enabled 2. Send these commands to NickServ /msg NickServ REGISTER /msg NickServ CERT ADD # Might be optional, but does not hurt 3. Edit connection settings: Username = REGISTER nick/username Password = REGISTER secret SASL authentication mechanism = Plain 4. Update (and reconnect) to the server --- Changes | 3 +++ lib/Convos/Core/Connection/Irc.pm | 30 +++++++++++++++++++++++------- t/irc-sasl.t | 14 +++++++++----- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/Changes b/Changes index 7ce712afa..1320af2e6 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ Revision history for perl distribution Convos +5.09 Not Released + - Fix SASL authentication process for Oragono IRCd (and others) #356 + 5.08 2021-01-05T12:21:00+0900 - Fix "convos version" after install/upgrade #539 - Fix reading notificatins with unicode diff --git a/lib/Convos/Core/Connection/Irc.pm b/lib/Convos/Core/Connection/Irc.pm index 36159fbee..31ceeb828 100644 --- a/lib/Convos/Core/Connection/Irc.pm +++ b/lib/Convos/Core/Connection/Irc.pm @@ -28,7 +28,9 @@ sub disconnect_p { my $p = Mojo::Promise->new; return $p->resolve({}) unless $self->{stream}; - $self->{disconnecting} = 1; # Prevent getting queued + $self->{myinfo}{authenticated} = false; + $self->{myinfo}{capabilities} = {}; + $self->{disconnecting} = 1; # Prevent getting queued $self->_write("QUIT :https://convos.chat", sub { $self->_stream_remove($p) }); return $p; } @@ -86,15 +88,29 @@ sub _connect_args_p { $self->_periodic_events; $url->port($params->param('tls') ? 6669 : 6667) unless $url->port; $params->param(nick => $self->nick) unless $params->param('nick'); - $self->{myinfo}{nick} = $params->param('nick'); + $self->{myinfo}{authenticated} = false; + $self->{myinfo}{capabilities} = {}; + $self->{myinfo}{nick} = $params->param('nick'); return $self->SUPER::_connect_args_p; } -sub _irc_event_903 { +sub _irc_event_900 { goto &_irc_event_sasl_status } # RPL_LOGGEDIN +sub _irc_event_901 { goto &_irc_event_sasl_status } # RPL_LOGGEDOUT +sub _irc_event_902 { goto &_irc_event_sasl_status } # ERR_NICKLOCKED +sub _irc_event_903 { goto &_irc_event_sasl_status } # RPL_SASLSUCCESS +sub _irc_event_904 { goto &_irc_event_sasl_status } # ERR_SASLFAIL +sub _irc_event_905 { goto &_irc_event_sasl_status } # ERR_SASLTOOLONG +sub _irc_event_906 { goto &_irc_event_sasl_status } # ERR_SASLABORTED +sub _irc_event_907 { goto &_irc_event_sasl_status } # ERR_SASLALREADY +sub _irc_event_908 { goto &_irc_event_cap } # RPL_SASLMECHS + +sub _irc_event_sasl_status { my ($self, $msg) = @_; + $msg->{error} = 1 if $msg->{command} =~ m!90[14567]!; $self->_irc_event_fallback($msg); - $self->_write("CAP END\r\n"); + $self->{myinfo}{authenticated} = $msg->{command} =~ m!90[03]! ? true : false; + $self->emit(state => me => $self->{myinfo}); } sub _irc_event_cap { @@ -107,11 +123,11 @@ sub _irc_event_cap { push @cap_req, 'sasl' if $self->{myinfo}{capabilities}{sasl} and $self->_sasl_mechanism; $self->_write(@cap_req ? sprintf "CAP REQ :%s\r\n", join ' ', @cap_req : "CAP END\r\n"); } - elsif ($msg->{raw_line} =~ m!\sACK\s:(.+)!) { + elsif ($msg->{raw_line} =~ m!\sACK\W*(.+)!) { my ($capabilities, $mech) = ($1, $self->_sasl_mechanism); $self->_write("AUTHENTICATE $mech\r\n") if $capabilities =~ m!\bsasl\b!; } - elsif ($msg->{raw_line} =~ m!\sNAC!) { + else { $self->_write("CAP END\r\n"); } } @@ -189,7 +205,7 @@ sub _irc_event_fallback { highlight => false, message => join(' ', @params), ts => time, - type => $msg->{command} =~ m!err! ? 'error' : 'notice', + type => $msg->{error} || $msg->{command} =~ m!err! ? 'error' : 'notice', } ); } diff --git a/t/irc-sasl.t b/t/irc-sasl.t index 1569f2700..c3a66984c 100644 --- a/t/irc-sasl.t +++ b/t/irc-sasl.t @@ -25,14 +25,15 @@ $server->client($connection)->server_event_ok('_irc_event_cap')->server_event_ok ->server_event_ok('_irc_event_authenticate')->server_write_ok(":example AUTHENTICATE +\r\n") ->client_event_ok('_irc_event_authenticate')->server_event_ok('_irc_event_authenticate') ->server_write_ok( - "example 900 superwoman superwoman!superwoman\@localhost superwoman :You are now logged in as superwoman\r\n" -)->server_write_ok(['welcome.irc'])->client_event_ok('_irc_event_rpl_welcome') - ->process_ok('capabilities handshake'); + ":server 900 superwoman superwoman!superwoman\@localhost superwoman :You are now logged in as superwoman\r\n" +)->client_event_ok('_irc_event_900')->server_write_ok(['welcome.irc']) + ->client_event_ok('_irc_event_rpl_welcome')->process_ok('capabilities handshake'); is_deeply( $connection->TO_JSON->{me}, { - capabilities => { + authenticated => true, + capabilities => { 'account-notify' => true, 'away-notify' => true, 'chghost' => true, @@ -50,6 +51,7 @@ is_deeply( note 'plain'; $connection->url->query->param(sasl => 'plain'); $connection->disconnect_p->then(sub { $connection->connect_p })->wait; +is $connection->TO_JSON->{me}{authenticated}, false, 'not authenticated after reconnect'; $server->client($connection)->server_event_ok('_irc_event_cap')->server_event_ok('_irc_event_nick') ->server_write_ok(":example CAP * LS * :account-notify away-notify chghost extended-join\r\n") @@ -59,8 +61,10 @@ $server->client($connection)->server_event_ok('_irc_event_cap')->server_event_ok ->server_event_ok('_irc_event_authenticate')->server_write_ok(":example AUTHENTICATE +\r\n") ->client_event_ok('_irc_event_authenticate')->server_event_ok('_irc_event_authenticate') ->server_write_ok( - "example 900 superwoman superwoman!superwoman\@localhost superwoman :You are now logged in as superwoman\r\n" + ":server 900 superwoman superwoman!superwoman\@localhost superwoman :You are now logged in as superwoman\r\n" )->server_write_ok(['welcome.irc'])->client_event_ok('_irc_event_rpl_welcome') ->process_ok('capabilities handshake'); +is $connection->TO_JSON->{me}{authenticated}, true, 'authenticated'; + done_testing;