Mercurial > dotfiles.old
diff irssi/scripts/cap_sasl.pl @ 148:4e92ca6c779a
add irssi conf
author | zegervdv <zegervdv@me.com> |
---|---|
date | Sat, 18 Oct 2014 10:06:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/irssi/scripts/cap_sasl.pl Sat Oct 18 10:06:58 2014 +0200 @@ -0,0 +1,272 @@ +use strict; +use Irssi; +use vars qw($VERSION %IRSSI); +# $Id$ + +use MIME::Base64; + +$VERSION = "1.1"; + +%IRSSI = ( + authors => 'Michael Tharp and Jilles Tjoelker', + contact => '[email protected]', + name => 'cap_sasl.pl', + description => 'Implements PLAIN SASL authentication mechanism for use with charybdis ircds, and enables CAP MULTI-PREFIX', + license => 'GNU General Public License', + url => 'http://sasl.charybdis.be/', +); + +my %sasl_auth = (); +my %mech = (); + +sub timeout; + +sub server_connected { + my $server = shift; + $server->send_raw_now("CAP LS"); +} + +sub event_cap { + my ($server, $args, $nick, $address) = @_; + my ($subcmd, $caps, $tosend); + + $tosend = ''; + if ($args =~ /^\S+ (\S+) :(.*)$/) { + $subcmd = uc $1; + $caps = ' '.$2.' '; + if ($subcmd eq 'LS') { + $tosend .= ' multi-prefix' if $caps =~ / multi-prefix /i; + $tosend .= ' sasl' if $caps =~ / sasl /i && defined($sasl_auth{$server->{tag}}); + $tosend =~ s/^ //; + $server->print('', "CLICAP: supported by server:$caps"); + if (!$server->{connected}) { + if ($tosend eq '') { + $server->send_raw_now("CAP END"); + } else { + $server->print('', "CLICAP: requesting: $tosend"); + $server->send_raw_now("CAP REQ :$tosend"); + } + } + Irssi::signal_stop(); + } elsif ($subcmd eq 'ACK') { + $server->print('', "CLICAP: now enabled:$caps"); + if ($caps =~ / sasl /i) { + $sasl_auth{$server->{tag}}{buffer} = ''; + if($mech{$sasl_auth{$server->{tag}}{mech}}) { + $server->send_raw_now("AUTHENTICATE " . $sasl_auth{$server->{tag}}{mech}); + Irssi::timeout_add_once(5000, \&timeout, $server->{tag}); + }else{ + $server->print('', 'SASL: attempted to start unknown mechanism "' . $sasl_auth{$server->{tag}}{mech} . '"'); + } + } + elsif (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } + Irssi::signal_stop(); + } elsif ($subcmd eq 'NAK') { + $server->print('', "CLICAP: refused:$caps"); + if (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } + Irssi::signal_stop(); + } elsif ($subcmd eq 'LIST') { + $server->print('', "CLICAP: currently enabled:$caps"); + Irssi::signal_stop(); + } + } +} + +sub event_authenticate { + my ($server, $args, $nick, $address) = @_; + my $sasl = $sasl_auth{$server->{tag}}; + return unless $sasl && $mech{$sasl->{mech}}; + + $sasl->{buffer} .= $args; + return if length($args) == 400; + + my $data = $sasl->{buffer} eq '+' ? '' : decode_base64($sasl->{buffer}); + my $out = $mech{$sasl->{mech}}($sasl, $data); + $out = '' unless defined $out; + $out = $out eq '' ? '+' : encode_base64($out, ''); + + while(length $out >= 400) { + my $subout = substr($out, 0, 400, ''); + $server->send_raw_now("AUTHENTICATE $subout"); + } + if(length $out) { + $server->send_raw_now("AUTHENTICATE $out"); + }else{ # Last piece was exactly 400 bytes, we have to send some padding to indicate we're done + $server->send_raw_now("AUTHENTICATE +"); + } + + $sasl->{buffer} = ''; + Irssi::signal_stop(); +} + +sub event_saslend { + my ($server, $args, $nick, $address) = @_; + + my $data = $args; + $data =~ s/^\S+ :?//; + # need this to see it, ?? -- jilles + $server->print('', $data); + if (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } +} + +sub timeout { + my $tag = shift; + my $server = Irssi::server_find_tag($tag); + if(!$server->{connected}) { + $server->print('', "SASL: authentication timed out"); + $server->send_raw_now("CAP END"); + } +} + +sub cmd_sasl { + my ($data, $server, $item) = @_; + + if ($data ne '') { + Irssi::command_runsub ('sasl', $data, $server, $item); + } else { + cmd_sasl_show(@_); + } +} + +sub cmd_sasl_set { + my ($data, $server, $item) = @_; + + if (my($net, $u, $p, $m) = $data =~ /^(\S+) (\S+) (\S+) (\S+)$/) { + if($mech{uc $m}) { + $sasl_auth{$net}{user} = $u; + $sasl_auth{$net}{password} = $p; + $sasl_auth{$net}{mech} = uc $m; + Irssi::print("SASL: added $net: [$m] $sasl_auth{$net}{user} *"); + }else{ + Irssi::print("SASL: unknown mechanism $m"); + } + } elsif ($data =~ /^(\S+)$/) { + $net = $1; + if (defined($sasl_auth{$net})) { + delete $sasl_auth{$net}; + Irssi::print("SASL: deleted $net"); + } else { + Irssi::print("SASL: no entry for $net"); + } + } else { + Irssi::print("SASL: usage: /sasl set <net> <user> <password or keyfile> <mechanism>"); + } +} + +sub cmd_sasl_show { + #my ($data, $server, $item) = @_; + my $net; + my $count = 0; + + foreach $net (keys %sasl_auth) { + Irssi::print("SASL: $net: [$sasl_auth{$net}{mech}] $sasl_auth{$net}{user} *"); + $count++; + } + Irssi::print("SASL: no networks defined") if !$count; +} + +sub cmd_sasl_save { + #my ($data, $server, $item) = @_; + my $file = Irssi::get_irssi_dir()."/sasl.auth"; + open FILE, "> $file" or return; + foreach my $net (keys %sasl_auth) { + printf FILE ("%s\t%s\t%s\t%s\n", $net, $sasl_auth{$net}{user}, $sasl_auth{$net}{password}, $sasl_auth{$net}{mech}); + } + close FILE; + Irssi::print("SASL: auth saved to $file"); +} + +sub cmd_sasl_load { + #my ($data, $server, $item) = @_; + my $file = Irssi::get_irssi_dir()."/sasl.auth"; + + open FILE, "< $file" or return; + %sasl_auth = (); + while (<FILE>) { + chomp; + my ($net, $u, $p, $m) = split (/\t/, $_, 4); + $m ||= "PLAIN"; + if($mech{uc $m}) { + $sasl_auth{$net}{user} = $u; + $sasl_auth{$net}{password} = $p; + $sasl_auth{$net}{mech} = uc $m; + }else{ + Irssi::print("SASL: unknown mechanism $m"); + } + } + close FILE; + Irssi::print("SASL: auth loaded from $file"); +} + +sub cmd_sasl_mechanisms { + Irssi::print("SASL: mechanisms supported: " . join(" ", keys %mech)); +} + +Irssi::signal_add_first('server connected', \&server_connected); +Irssi::signal_add('event cap', \&event_cap); +Irssi::signal_add('event authenticate', \&event_authenticate); +Irssi::signal_add('event 903', 'event_saslend'); +Irssi::signal_add('event 904', 'event_saslend'); +Irssi::signal_add('event 905', 'event_saslend'); +Irssi::signal_add('event 906', 'event_saslend'); +Irssi::signal_add('event 907', 'event_saslend'); + +Irssi::command_bind('sasl', \&cmd_sasl); +Irssi::command_bind('sasl load', \&cmd_sasl_load); +Irssi::command_bind('sasl save', \&cmd_sasl_save); +Irssi::command_bind('sasl set', \&cmd_sasl_set); +Irssi::command_bind('sasl show', \&cmd_sasl_show); +Irssi::command_bind('sasl mechanisms', \&cmd_sasl_mechanisms); + +$mech{PLAIN} = sub { + my($sasl, $data) = @_; + my $u = $sasl->{user}; + my $p = $sasl->{password}; + + join("\0", $u, $u, $p); +}; + +eval { + use Crypt::OpenSSL::Bignum; + use Crypt::DH; + use Crypt::Blowfish; + use Math::BigInt; + sub bin2bi { return Crypt::OpenSSL::Bignum->new_from_bin(shift)->to_decimal } # binary to BigInt + sub bi2bin { return Crypt::OpenSSL::Bignum->new_from_decimal((shift)->bstr)->to_bin } # BigInt to binary + $mech{'DH-BLOWFISH'} = sub { + my($sasl, $data) = @_; + my $u = $sasl->{user}; + my $pass = $sasl->{password}; + + # Generate private key and compute secret key + my($p, $g, $y) = unpack("(n/a*)3", $data); + my $dh = Crypt::DH->new(p => bin2bi($p), g => bin2bi($g)); + $dh->generate_keys; + + my $secret = bi2bin($dh->compute_secret(bin2bi($y))); + my $pubkey = bi2bin($dh->pub_key); + + # Pad the password to the nearest multiple of blocksize and encrypt + $pass .= "\0"; + $pass .= chr(rand(256)) while length($pass) % 8; + + my $cipher = Crypt::Blowfish->new($secret); + my $crypted = ''; + while(length $pass) { + my $clear = substr($pass, 0, 8, ''); + $crypted .= $cipher->encrypt($clear); + } + + pack("n/a*Z*a*", $pubkey, $u, $crypted); + }; +}; + +cmd_sasl_load(); + +# vim: ts=4