diff options
| -rw-r--r-- | _data/scripts.yaml | 4 | ||||
| -rw-r--r-- | scripts/cap_sasl.pl | 106 |
2 files changed, 77 insertions, 33 deletions
diff --git a/_data/scripts.yaml b/_data/scripts.yaml index 0e48e8a..85520af 100644 --- a/_data/scripts.yaml +++ b/_data/scripts.yaml @@ -727,12 +727,12 @@ contact: "grawity@gmail.com" description: "Implements SASL authentication (and multi-prefix as free bonus)" filename: "cap_sasl.pl" - modified: "2014-12-18 22:58:00" + modified: "2015-01-07 06:03" modules: "Crypt::PK::ECC MIME::Base64" license: "GPLv2" name: "cap_sasl" url: "https://nullroute.eu.org/~grawity/irssi.html" - version: "1.9" + version: "1.10" - authors: "ZaMz0n" commands: "cddb" diff --git a/scripts/cap_sasl.pl b/scripts/cap_sasl.pl index a9dc237..7c1fba2 100644 --- a/scripts/cap_sasl.pl +++ b/scripts/cap_sasl.pl @@ -4,7 +4,7 @@ use MIME::Base64; use vars qw($VERSION %IRSSI); use constant CHALLENGE_SIZE => 32; -$VERSION = "1.9"; +$VERSION = "1.10"; %IRSSI = ( authors => 'Michael Tharp (gxti), Jilles Tjoelker (jilles), Mantas Mikulėnas (grawity)', contact => 'grawity@gmail.com', @@ -17,6 +17,15 @@ $VERSION = "1.9"; my %sasl_auth = (); my %mech = (); +sub irssi_abspath { + my $f = shift; + $f =~ s!^~/!$ENV{HOME}/!; + if ($f !~ m!^/!) { + $f = Irssi::get_irssi_dir()."/".$f; + } + return $f; +} + sub timeout; sub server_connected { @@ -269,20 +278,20 @@ $mech{EXTERNAL} = sub { }; if (eval {require Crypt::PK::ECC}) { + my $mech = "ECDSA-NIST256P-CHALLENGE"; + $mech{'ECDSA-NIST256P-CHALLENGE'} = sub { my ($sasl, $data) = @_; my $u = $sasl->{user}; - my $k = $sasl->{password}; - if ($k !~ m!^/!) { - $k = Irssi::get_irssi_dir()."/".$k; - } - if (!-f $k) { - Irssi::print("SASL: key file '$k' not found", MSGLEVEL_CLIENTERROR); + my $f = $sasl->{password}; + $f = irssi_abspath($f); + if (!-f $f) { + Irssi::print("SASL: key file '$f' not found", MSGLEVEL_CLIENTERROR); return; } - my $pk = eval {Crypt::PK::ECC->new($k)}; + my $pk = eval {Crypt::PK::ECC->new($f)}; if ($@ || !$pk || !$pk->is_private) { - Irssi::print("SASL: no private key in file '$k'", MSGLEVEL_CLIENTERROR); + Irssi::print("SASL: no private key in file '$f'", MSGLEVEL_CLIENTERROR); return; } my $step = ++$sasl->{step}; @@ -308,20 +317,14 @@ if (eval {require Crypt::PK::ECC}) { sub cmd_sasl_keygen { my ($data, $server, $witem) = @_; - my $mech = "ECDSA-NIST256P-CHALLENGE"; - my $net; - my $print; - - if ($server) { - $net = $server->{tag}; - $print = sub { $server->print("", shift) }; - } else { - $net = $data; - $print = sub { Irssi::print(shift) }; - } + my $print = $server + ? sub { $server->print("", shift, shift // MSGLEVEL_CLIENTNOTICE) } + : sub { Irssi::print(shift, shift // MSGLEVEL_CLIENTNOTICE) }; + my $net = $server ? $server->{tag} : $data; if (!length $net) { - $print->("Please connect to a server first."); + Irssi::print("SASL: please connect to a server first", + MSGLEVEL_CLIENTERROR); return; } @@ -330,7 +333,7 @@ if (eval {require Crypt::PK::ECC}) { my $f_priv = Irssi::get_irssi_dir()."/$f_name.key"; my $f_pub = Irssi::get_irssi_dir()."/$f_name.pub"; if (-e $f_priv) { - $print->("SASL: refusing to overwrite '$f_priv'"); + $print->("SASL: refusing to overwrite '$f_priv'", MSGLEVEL_CLIENTERROR); return; } @@ -341,16 +344,13 @@ if (eval {require Crypt::PK::ECC}) { my $priv = $pk->export_key_pem("private"); my $pub = encode_base64($pk->export_key_raw("public_compressed"), ""); - my $cmdchar = substr(Irssi::settings_get_str("cmdchars"), 0, 1); - my $cmd = "msg NickServ SET PROPERTY pubkey $pub"; - if (open(my $fh, ">", $f_priv)) { chmod(0600, $f_priv); print $fh $priv; close($fh); $print->("SASL: wrote private key to '$f_priv'"); } else { - $print->("SASL: could not write '$f_priv': $!"); + $print->("SASL: could not write '$f_priv': $!", MSGLEVEL_CLIENTERROR); return; } @@ -358,28 +358,72 @@ if (eval {require Crypt::PK::ECC}) { print $fh $pub."\n"; close($fh); } else { - $print->("SASL: could not write '$f_pub': $!"); + $print->("SASL: could not write '$f_pub': $!", MSGLEVEL_CLIENTERROR); } + my $cmdchar = substr(Irssi::settings_get_str("cmdchars"), 0, 1); + my $cmd = "msg NickServ SET PROPERTY pubkey $pub"; + # TODO: change to 'SET PUBKEY' when freenode gets support for that + if ($server) { $print->("SASL: updating your Irssi settings..."); $sasl_auth{$net}{user} //= $server->{nick}; $sasl_auth{$net}{password} = "$f_name.key"; $sasl_auth{$net}{mech} = $mech; cmd_sasl_save(@_); - - $print->("SASL: submitting key to NickServ..."); + $print->("SASL: submitting pubkey to NickServ..."); $server->command($cmd); } else { $print->("SASL: update your Irssi settings:"); $print->("%P".$cmdchar."sasl set $net <nick> $f_name.key $mech"); - - $print->("SASL: submit your public key to $net:"); + $print->("SASL: submit your pubkey to $net:"); $print->("%P".$cmdchar.$cmd); } } + sub cmd_sasl_pubkey { + my ($data, $server, $witem) = @_; + + my $arg = $server ? $server->{tag} : $data; + + my $f; + if (!length $arg) { + Irssi::print("SASL: please select a server or specify a keyfile path", + MSGLEVEL_CLIENTERROR); + return; + } elsif ($arg =~ m![/.]!) { + $f = $arg; + } else { + if ($sasl_auth{$arg}{mech} eq $mech) { + $f = $sasl_auth{$arg}{password}; + } else { + $f = lc "sasl-ecdsa-$arg"; + $f =~ s![ /]+!_!g; + $f = "$f.key"; + } + } + + $f = irssi_abspath($f); + if (!-e $f) { + Irssi::print("SASL: keyfile '$f' not found", MSGLEVEL_CLIENTERROR); + return; + } + + my $pk = eval {Crypt::PK::ECC->new($f)}; + if ($@ || !$pk || !$pk->is_private) { + Irssi::print("SASL: no private key in file '$f'", MSGLEVEL_CLIENTERROR); + Irssi::print("(keys using named parameters or PKCS#8 are not yet supported)", + MSGLEVEL_CLIENTERROR); + return; + } + + my $pub = encode_base64($pk->export_key_raw("public_compressed"), ""); + Irssi::print("SASL: loaded keyfile '$f'"); + Irssi::print("SASL: your pubkey is $pub"); + } + Irssi::command_bind('sasl keygen', \&cmd_sasl_keygen); + Irssi::command_bind('sasl pubkey', \&cmd_sasl_pubkey); }; cmd_sasl_load(); |
