Advertisement
Guest User

Untitled

a guest
Feb 7th, 2010
1,169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.79 KB | None | 0 0
  1. use strict;
  2. use Irssi;
  3. use vars qw($VERSION %IRSSI);
  4. # $Id: cap_sasl.pl 5330 2006-05-31 02:25:21Z gxti $
  5.  
  6. use MIME::Base64;
  7.  
  8. $VERSION = "1.2";
  9.  
  10. %IRSSI = (
  11. authors => 'Michael Tharp, Jilles Tjoelker, Lee T. Starnes',
  12. contact => 'lstarnes1024+irc@gmail.com', 'gxti@partiallystapled.com',
  13. name => 'cap_sasl.pl',
  14. description => 'Implements PLAIN SASL authentication mechanism for use with charybdis ircds, and enables CAP MULTI-PREFIX',
  15. license => 'GNU General Public License',
  16. url => 'http://sasl.charybdis.be/',
  17. changed => 'Sun Feb 7 21:27:35 EST 2010'
  18. );
  19.  
  20. my %sasl_auth = ();
  21. my %mech = ();
  22.  
  23. sub timeout;
  24.  
  25. sub server_connected {
  26. my $server = shift;
  27. $server->send_raw_now("CAP LS");
  28. }
  29.  
  30. sub event_cap {
  31. my ($server, $args, $nick, $address) = @_;
  32. my ($subcmd, $caps, $tosend);
  33.  
  34. my $timeout = Irssi::settings_get_int('sasl_timeout') * 1000;
  35. $tosend = '';
  36. if ($args =~ /^\S+ (\S+) :(.*)$/) {
  37. $subcmd = uc $1;
  38. $caps = ' '.$2.' ';
  39. if ($subcmd eq 'LS') {
  40. $tosend .= ' multi-prefix' if $caps =~ / multi-prefix /i;
  41. $tosend .= ' sasl' if $caps =~ / sasl /i && defined($sasl_auth{$server->{tag}});
  42. $tosend =~ s/^ //;
  43. $server->print('', "CLICAP: supported by server:$caps");
  44. if (!$server->{connected}) {
  45. if ($tosend eq '') {
  46. $server->send_raw_now("CAP END");
  47. } else {
  48. $server->print('', "CLICAP: requesting: $tosend");
  49. $server->send_raw_now("CAP REQ :$tosend");
  50. }
  51. }
  52. Irssi::signal_stop();
  53. } elsif ($subcmd eq 'ACK') {
  54. $server->print('', "CLICAP: now enabled:$caps");
  55. if ($caps =~ / sasl /i) {
  56. $sasl_auth{$server->{tag}}{buffer} = '';
  57. if($mech{$sasl_auth{$server->{tag}}{mech}}) {
  58. $server->send_raw_now("AUTHENTICATE " . $sasl_auth{$server->{tag}}{mech});
  59. Irssi::timeout_add_once($timeout, \&timeout, $server->{tag});
  60. }else{
  61. $server->print('', 'SASL: attempted to start unknown mechanism "' . $sasl_auth{$server->{tag}}{mech} . '"');
  62. }
  63. }
  64. elsif (!$server->{connected}) {
  65. $server->send_raw_now("CAP END");
  66. }
  67. Irssi::signal_stop();
  68. } elsif ($subcmd eq 'NAK') {
  69. $server->print('', "CLICAP: refused:$caps");
  70. if (!$server->{connected}) {
  71. $server->send_raw_now("CAP END");
  72. }
  73. Irssi::signal_stop();
  74. } elsif ($subcmd eq 'LIST') {
  75. $server->print('', "CLICAP: currently enabled:$caps");
  76. Irssi::signal_stop();
  77. }
  78. }
  79. }
  80.  
  81. sub event_authenticate {
  82. my ($server, $args, $nick, $address) = @_;
  83. my $sasl = $sasl_auth{$server->{tag}};
  84. return unless $sasl && $mech{$sasl->{mech}};
  85.  
  86. $sasl->{buffer} .= $args;
  87. return if length($args) == 400;
  88.  
  89. my $data = $sasl->{buffer} eq '+' ? '' : decode_base64($sasl->{buffer});
  90. my $out = $mech{$sasl->{mech}}($sasl, $data);
  91. $out = '' unless defined $out;
  92. $out = $out eq '' ? '+' : encode_base64($out, '');
  93.  
  94. while(length $out >= 400) {
  95. my $subout = substr($out, 0, 400, '');
  96. $server->send_raw_now("AUTHENTICATE $subout");
  97. }
  98. if(length $out) {
  99. $server->send_raw_now("AUTHENTICATE $out");
  100. }else{ # Last piece was exactly 400 bytes, we have to send some padding to indicate we're done
  101. $server->send_raw_now("AUTHENTICATE +");
  102. }
  103.  
  104. $sasl->{buffer} = '';
  105. Irssi::signal_stop();
  106. }
  107.  
  108. sub event_saslend {
  109. my ($server, $args, $nick, $address) = @_;
  110.  
  111. my $data = $args;
  112. $data =~ s/^\S+ :?//;
  113. # need this to see it, ?? -- jilles
  114. $server->print('', $data);
  115. if (!$server->{connected}) {
  116. $server->send_raw_now("CAP END");
  117. }
  118. }
  119.  
  120. sub timeout {
  121. my $tag = shift;
  122. my $server = Irssi::server_find_tag($tag);
  123. if(!$server->{connected}) {
  124. $server->print('', "SASL: authentication timed out");
  125. $server->send_raw_now("CAP END");
  126. }
  127. }
  128.  
  129. sub cmd_sasl {
  130. my ($data, $server, $item) = @_;
  131.  
  132. if ($data ne '') {
  133. Irssi::command_runsub ('sasl', $data, $server, $item);
  134. } else {
  135. cmd_sasl_show(@_);
  136. }
  137. }
  138.  
  139. sub cmd_sasl_set {
  140. my ($data, $server, $item) = @_;
  141.  
  142. if (my($net, $u, $p, $m) = $data =~ /^(\S+) (\S+) (\S+) (\S+)$/) {
  143. if($mech{uc $m}) {
  144. $sasl_auth{$net}{user} = $u;
  145. $sasl_auth{$net}{password} = $p;
  146. $sasl_auth{$net}{mech} = uc $m;
  147. Irssi::print("SASL: added $net: [$m] $sasl_auth{$net}{user} *");
  148. }else{
  149. Irssi::print("SASL: unknown mechanism $m");
  150. }
  151. } elsif ($data =~ /^(\S+)$/) {
  152. $net = $1;
  153. if (defined($sasl_auth{$net})) {
  154. delete $sasl_auth{$net};
  155. Irssi::print("SASL: deleted $net");
  156. } else {
  157. Irssi::print("SASL: no entry for $net");
  158. }
  159. } else {
  160. Irssi::print("SASL: usage: /sasl set <net> <user> <password or keyfile> <mechanism>");
  161. }
  162. }
  163.  
  164. sub cmd_sasl_show {
  165. #my ($data, $server, $item) = @_;
  166. my $net;
  167. my $count = 0;
  168.  
  169. foreach $net (keys %sasl_auth) {
  170. Irssi::print("SASL: $net: [$sasl_auth{$net}{mech}] $sasl_auth{$net}{user} *");
  171. $count++;
  172. }
  173. Irssi::print("SASL: no networks defined") if !$count;
  174. }
  175.  
  176. sub cmd_sasl_save {
  177. #my ($data, $server, $item) = @_;
  178. my $file = Irssi::get_irssi_dir()."/sasl.auth";
  179. open FILE, "> $file" or return;
  180. foreach my $net (keys %sasl_auth) {
  181. printf FILE ("%s\t%s\t%s\t%s\n", $net, $sasl_auth{$net}{user}, $sasl_auth{$net}{password}, $sasl_auth{$net}{mech});
  182. }
  183. close FILE;
  184. Irssi::print("SASL: auth saved to $file");
  185. }
  186.  
  187. sub cmd_sasl_load {
  188. #my ($data, $server, $item) = @_;
  189. my $file = Irssi::get_irssi_dir()."/sasl.auth";
  190.  
  191. open FILE, "< $file" or return;
  192. %sasl_auth = ();
  193. while (<FILE>) {
  194. chomp;
  195. my ($net, $u, $p, $m) = split (/\t/, $_, 4);
  196. $m ||= "PLAIN";
  197. if($mech{uc $m}) {
  198. $sasl_auth{$net}{user} = $u;
  199. $sasl_auth{$net}{password} = $p;
  200. $sasl_auth{$net}{mech} = uc $m;
  201. }else{
  202. Irssi::print("SASL: unknown mechanism $m");
  203. }
  204. }
  205. close FILE;
  206. Irssi::print("SASL: auth loaded from $file");
  207. }
  208.  
  209. sub cmd_sasl_mechanisms {
  210. Irssi::print("SASL: mechanisms supported: " . join(" ", keys %mech));
  211. }
  212.  
  213. Irssi::signal_add_first('server connected', \&server_connected);
  214. Irssi::signal_add('event cap', \&event_cap);
  215. Irssi::signal_add('event authenticate', \&event_authenticate);
  216. Irssi::signal_add('event 903', 'event_saslend');
  217. Irssi::signal_add('event 904', 'event_saslend');
  218. Irssi::signal_add('event 905', 'event_saslend');
  219. Irssi::signal_add('event 906', 'event_saslend');
  220. Irssi::signal_add('event 907', 'event_saslend');
  221.  
  222. Irssi::command_bind('sasl', \&cmd_sasl);
  223. Irssi::command_bind('sasl load', \&cmd_sasl_load);
  224. Irssi::command_bind('sasl save', \&cmd_sasl_save);
  225. Irssi::command_bind('sasl set', \&cmd_sasl_set);
  226. Irssi::command_bind('sasl show', \&cmd_sasl_show);
  227. Irssi::command_bind('sasl mechanisms', \&cmd_sasl_mechanisms);
  228.  
  229. Irssi::settings_add_int('misc', 'sasl_timeout', 5);
  230.  
  231. $mech{PLAIN} = sub {
  232. my($sasl, $data) = @_;
  233. my $u = $sasl->{user};
  234. my $p = $sasl->{password};
  235.  
  236. join("\0", $u, $u, $p);
  237. };
  238.  
  239. eval {
  240. use Crypt::OpenSSL::Bignum;
  241. use Crypt::DH;
  242. use Crypt::Blowfish;
  243. use Math::BigInt;
  244. sub bin2bi { return Crypt::OpenSSL::Bignum->new_from_bin(shift)->to_decimal } # binary to BigInt
  245. sub bi2bin { return Crypt::OpenSSL::Bignum->new_from_decimal((shift)->bstr)->to_bin } # BigInt to binary
  246. $mech{'DH-BLOWFISH'} = sub {
  247. my($sasl, $data) = @_;
  248. my $u = $sasl->{user};
  249. my $pass = $sasl->{password};
  250.  
  251. # Generate private key and compute secret key
  252. my($p, $g, $y) = unpack("(n/a*)3", $data);
  253. my $dh = Crypt::DH->new(p => bin2bi($p), g => bin2bi($g));
  254. $dh->generate_keys;
  255.  
  256. my $secret = bi2bin($dh->compute_secret(bin2bi($y)));
  257. my $pubkey = bi2bin($dh->pub_key);
  258.  
  259. # Pad the password to the nearest multiple of blocksize and encrypt
  260. $pass .= "\0";
  261. $pass .= chr(rand(256)) while length($pass) % 8;
  262.  
  263. my $cipher = Crypt::Blowfish->new($secret);
  264. my $crypted = '';
  265. while(length $pass) {
  266. my $clear = substr($pass, 0, 8, '');
  267. $crypted .= $cipher->encrypt($clear);
  268. }
  269.  
  270. pack("n/a*Z*a*", $pubkey, $u, $crypted);
  271. };
  272. };
  273.  
  274. cmd_sasl_load();
  275.  
  276. # vim: ts=4
  277.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement