diff --git a/bin/auto b/bin/auto index 98266e7..0d534d6 100755 --- a/bin/auto +++ b/bin/auto @@ -260,7 +260,7 @@ foreach my $cskey (keys %cservers) { err(2, "Failed to connect to server: ".$cskey." [".$cservers{$cskey}{'host'}[0].":".$cservers{$cskey}{'port'}[0]."]", 0) and next; } - API::Std::event_run("on_preconnect"); + API::Std::event_run("on_preconnect", $cskey); # Send USER and NICK. socksnd($cskey, "USER ".$cservers{$cskey}{'ident'}[0]." * * :".$cservers{$cskey}{'realname'}[0]) or err(2, "Failed to connect to server: ".$cskey." [".$cservers{$cskey}{'host'}[0].":".$cservers{$cskey}{'port'}[0]."]", 0) diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 11a30b5..1073b43 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -3,6 +3,8 @@ Auto IRC Bot 3.0: Change Log 3.0 Indev ================================================================== + * Added mod_exists() to API::Std. + * Added SASL support with modules/SASLAuth. * Added mdelete() to DB/Flatfile. * Added topic() to API::IRC. * Added has_priv() to API::Std. diff --git a/modules/SASLAuth.pm b/modules/SASLAuth.pm new file mode 100644 index 0000000..bf48890 --- /dev/null +++ b/modules/SASLAuth.pm @@ -0,0 +1,190 @@ +# Auto IRC Bot. An advanced, lightweight and powerful IRC bot. +# Copyright (C) 2010-2011 Xelhua Development Team (doc/CREDITS) +# This program is free software; rights to this code are stated in doc/LICENSE. +package m_SASLAuth; +use strict; +use warnings; +use feature qw(switch); +use MIME::Base64; +use API::Std qw(hook_add hook_del rchook_add rchook_del conf_get err awarn timer_add timer_del); +use API::IRC qw(privmsg); + + +# Initialization subroutine. +sub _init +{ + # Check if this Auto was built with SASL support. + err(2, "Auto was not built with SASL support. Aborting SASLAuth.", 0) and return 0 if $Auto::ENFEAT !~ /sasl/; + # Add a hook for before we connect. + hook_add('on_preconnect', 'CAP', sub { my ($srv) = @_; Auto::socksnd($srv, 'CAP LS'); } + ) or return 0; + # Hook for parsing CAP. + rchook_add('CAP', \&m_SASLAuth::handle_cap) or return 0; + # Hook for parsing 903. + rchook_add('903', \&m_SASLAuth::handle_903) or return 0; + # Hook for parsing 904. + rchook_add('904', \&m_SASLAuth::handle_904) or return 0; + # Hook for parsing 906. + rchook_add('906', \&m_SASLAuth::handle_906) or return 0; + return 1; +} + +# Void subroutine. +sub _void +{ + # Delete the hooks. + hook_del("on_preconnect", "CAP") or return 0; + rchook_del('CAP'); + rchook_del('903'); + rchool_del('904'); + rchool_del('906'); + return 1; +} + +sub handle_cap { + my ($srv, @parv) = @_; + my $line = join(' ',@parv); + my ($tosend); + + given ($line) { + when (/ LS /) { + $tosend .= ' multi-prefix' if $line =~ /multi-prefix/i; + $tosend .= ' sasl' if $line =~ /sasl/ and conf_get("server:$srv:sasl_username"); + awarn(2, "SASL is unavailable on this server.") if $tosend !~ /sasl/; + if ($tosend eq '') { Auto::socksnd($srv, 'CAP END') } + else { Auto::socksnd($srv, "CAP REQ :$tosend"); } + } + when (/ ACK /) { + if ( $line =~ /sasl/) { + Auto::socksnd($srv, 'AUTHENTICATE PLAIN'); + timer_add('auth_timeout', 1, (conf_get("server:$srv:sasl_timeout"))[0][0], sub { Auto::socksnd($srv, 'CAP END'); }); + } + else { + Auto::socksnd($srv, 'CAP END'); + awarn(2, "SASL authentication failed at ACK"); + } + } + when (/ NAK /) { + Auto::socksnd($srv, 'CAP END'); + awarn(2, "SASL authentication failed. Server refused ".$tosend); + } + } + return 1; +} + +# Parse: AUTHENTICATE +sub handle_authenticate +{ + my ($srv, @parv) = @_; + my $u = (conf_get("server:$srv:sasl_username"))[0][0]; + my $p = (conf_get("server:$srv:sasl_password"))[0][0]; + my $out = join( "\0", $u, $u, $p ); + $out = encode_base64( $out, "" ); + + if ( length $out == 0 ) { + Auto::socksnd($srv, "AUTHENTICATE +"); + return; + } + else { + while ( length $out >= 400 ) { + my $subout = substr( $out, 0, 400, '' ); + Auto::socksnd($srv, "AUTHENTICATE $subout"); + } + if ( length $out ) { + Auto::socksnd($srv, "AUTHENTICATE $out"); + } + else { + Auto::socksnd($srv, "AUTHENTICATE +"); + } + } + return 1; +} + +# Parse: Numeric:903 +# SASL authentication successful. +sub handle_903 +{ + my ($srv, undef) = @_; + Auto::socksnd($srv, 'CAP END'); + timer_del('auth_timeout'); +} + +# Parse: Numeric:904 +# SASL authentication failed. +sub handle_904 +{ + my ($srv, undef) = @_; + Auto::socksnd($srv, 'CAP END'); + timer_del('auth_timeout'); + awarn(2, "SASL authentication failed!"); +} + +# Parse: Numeric:906 +# SASL authentication aborted. +sub handle_906 +{ + my ($svr, undef) = @_; + Auto::socksnd($svr, 'CAP END'); + timer_del('auth_timeout'); + awarn(2, "SASL authentication aborted!"); +} + +# Start initialization. +API::Std::mod_init("SASLAuth", "Xelhua", "1.00", "3.0d", __PACKAGE__); + +__END__ + +=head1 SASL Auth + +=head2 Description + +=over + +This module adds support for IRCv3 SASL authentication, a nicer way of authenticating +to services and/or the IRCd. + +SASL is available in Charybdis IRCd's and InspIRCd 1.2+ with m_cap and m_sasl. Atheme +IRC Services also allows you to identify to NickServ/UserServ with SASL. + +=back + +=head2 How To Use + +=over + +To use SASLAuth, first add it to module auto-load then add the following to the server +block(s) you wish to use SASL with: + +sasl_username "services accountname"; +sasl_password "services password"; +sasl_timeout ; + +=back + +=head2 Examples + +=over + +sasl_username "JohnBot"; +sasl_password "foobar12345"; +sasl_timeout 20; + +=back + +=head2 To Do + +=over + +* Add support for SASL mechanism DH-BLOWFISH. + +=back + +=head2 Technical + +=over + +This adds an extra dependency: You must build Auto with the --enable-sasl option. + +THis module is compatible with Auto v3.0alpha1+. + +=back diff --git a/src/API/Std.pm b/src/API/Std.pm index d96b115..5ffec1f 100644 --- a/src/API/Std.pm +++ b/src/API/Std.pm @@ -55,6 +55,15 @@ sub mod_init } } +# Check if a module exists. +sub mod_exists +{ + my ($name) = @_; + + return 1 if defined $MODULE{$name}; + return 0; +} + # Void a module. sub mod_void { diff --git a/src/Parser/IRC.pm b/src/Parser/IRC.pm index 49dc92c..bd42ab4 100644 --- a/src/Parser/IRC.pm +++ b/src/Parser/IRC.pm @@ -57,6 +57,12 @@ sub ircparse # send a PONG. Auto::socksnd($svr, "PONG ".$ex[1]); } + # If it's AUTHENTICATE + elsif ($ex[0] eq 'AUTHENTICATE') { + if (API::Std::mod_exists("SASLAuth")) { + m_SASLAuth::handle_authenticate($svr, @ex); + } + } else { # otherwise, check %RAWC for ex[1]. if (defined $RAWC{$ex[1]}) {