ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Transport.pm
(Generate patch)

Comparing AnyEvent-MP/MP/Transport.pm (file contents):
Revision 1.8 by root, Mon Aug 3 14:58:13 2009 UTC vs.
Revision 1.19 by root, Tue Aug 4 21:36:28 2009 UTC

1=head1 NAME 1=head1 NAME
2 2
3AnyEvent::MP::Transport - actual transport protocol 3AnyEvent::MP::Transport - actual transport protocol handler
4 4
5=head1 SYNOPSIS 5=head1 SYNOPSIS
6 6
7 use AnyEvent::MP::Transport; 7 use AnyEvent::MP::Transport;
8 8
9=head1 DESCRIPTION 9=head1 DESCRIPTION
10 10
11This is the superclass for MP transports, most of which is considered an 11This implements the actual transport protocol for MP (it represents a
12implementation detail. 12single link), most of which is considered an implementation detail.
13 13
14See the "PROTOCOL" section below if you want to write another client for 14See the "PROTOCOL" section below if you want to write another client for
15this protocol. 15this protocol.
16 16
17=head1 FUNCTIONS/METHODS 17=head1 FUNCTIONS/METHODS
27use Scalar::Util; 27use Scalar::Util;
28use MIME::Base64 (); 28use MIME::Base64 ();
29use Storable (); 29use Storable ();
30use JSON::XS (); 30use JSON::XS ();
31 31
32use Digest::MD6 ();
33use Digest::HMAC_MD6 ();
34
32use AE (); 35use AE ();
33use AnyEvent::Socket (); 36use AnyEvent::Socket ();
34use AnyEvent::Handle (); 37use AnyEvent::Handle ();
35 38
36use base Exporter::; 39use base Exporter::;
43Creates a listener on the given host/port using 46Creates a listener on the given host/port using
44C<AnyEvent::Socket::tcp_server>. 47C<AnyEvent::Socket::tcp_server>.
45 48
46See C<new>, below, for constructor arguments. 49See C<new>, below, for constructor arguments.
47 50
48Defaults for peerhost, peerport, fh and tls are provided. 51Defaults for peerhost, peerport and fh are provided.
49 52
50=cut 53=cut
51 54
52sub mp_server($$@) { 55sub mp_server($$@) {
53 my $cb = pop; 56 my $cb = pop;
58 61
59 $cb->(new AnyEvent::MP::Transport 62 $cb->(new AnyEvent::MP::Transport
60 fh => $fh, 63 fh => $fh,
61 peerhost => $host, 64 peerhost => $host,
62 peerport => $port, 65 peerport => $port,
63 tls => "accept",
64 @args, 66 @args,
65 ); 67 );
66 } 68 }
67} 69}
68 70
82 $cb->(new AnyEvent::MP::Transport 84 $cb->(new AnyEvent::MP::Transport
83 fh => $fh, 85 fh => $fh,
84 peername => $host, 86 peername => $host,
85 peerhost => $nhost, 87 peerhost => $nhost,
86 peerport => $nport, 88 peerport => $nport,
87 tls => "accept",
88 @args, 89 @args,
89 ); 90 );
90 } 91 }
91} 92}
92 93
105 on_eof => sub { clean-close-callback }, 106 on_eof => sub { clean-close-callback },
106 on_connect => sub { successful-connect-callback }, 107 on_connect => sub { successful-connect-callback },
107 greeting => { key => value }, 108 greeting => { key => value },
108 109
109 # tls support 110 # tls support
110 tls => "accept|connect",
111 tls_ctx => AnyEvent::TLS, 111 tls_ctx => AnyEvent::TLS,
112 peername => $peername, # for verification 112 peername => $peername, # for verification
113 ; 113 ;
114 114
115=cut 115=cut
116 116
117our @FRAMINGS = qw(json storable); # the framing types we accept and send, in order of preference 117our @FRAMINGS = qw(json storable); # the framing types we accept and send, in order of preference
118our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send 118our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send
119our @AUTH_RCV = (@AUTH_SND, qw(hex_secret)); # auth types we accept 119our @AUTH_RCV = (@AUTH_SND, qw(cleartext)); # auth types we accept
120 120
121#AnyEvent::Handle::register_write_type mp_record => sub { 121#AnyEvent::Handle::register_write_type mp_record => sub {
122#}; 122#};
123 123
124sub new { 124sub new {
129 $self->{queue} = []; 129 $self->{queue} = [];
130 130
131 { 131 {
132 Scalar::Util::weaken (my $self = $self); 132 Scalar::Util::weaken (my $self = $self);
133 133
134 if (exists $arg{connect}) {
135 $arg{tls_ctx} ||= { sslv2 => 0, sslv3 => 0, tlsv1 => 1, verify => 1 };
136 }
137
138 $arg{secret} = AnyEvent::MP::Base::default_secret () 134 $arg{secret} = AnyEvent::MP::Base::default_secret ()
139 unless exists $arg{secret}; 135 unless exists $arg{secret};
140 136
137 my $secret = $arg{secret};
138
139 if ($secret =~ /-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/s) {
140 # assume TLS mode
141 $arg{tls_ctx} = {
142 sslv2 => 0,
143 sslv3 => 0,
144 tlsv1 => 1,
145 verify => 1,
146 cert => $secret,
147 ca_cert => $secret,
148 verify_require_client_cert => 1,
149 };
150 }
151
141 $self->{hdl} = new AnyEvent::Handle 152 $self->{hdl} = new AnyEvent::Handle
142 fh => delete $arg{fh}, 153 fh => delete $arg{fh},
143 rbuf_max => 64 * 1024,
144 autocork => 1, 154 autocork => 1,
145 no_delay => 1, 155 no_delay => 1,
146 on_error => sub { 156 on_error => sub {
147 $self->error ($_[2]); 157 $self->error ($_[2]);
148 }, 158 },
149 peername => delete $arg{peername}, 159 peername => delete $arg{peername},
150 ; 160 ;
151 161
152 my $secret = $arg{secret};
153 my $greeting_kv = $self->{greeting} ||= {}; 162 my $greeting_kv = $self->{greeting} ||= {};
154 $greeting_kv->{"tls"} = "1.0" 163 $greeting_kv->{"tls"} = "1.0"
155 if $arg{tls_ctx}; 164 if $arg{tls_ctx};
156 $greeting_kv->{provider} = "AE-$VERSION"; 165 $greeting_kv->{provider} = "AE-$VERSION";
157 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport}; 166 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport};
158 167
159 # send greeting 168 # send greeting
160 my $lgreeting1 = "aemp;$PROTOCOL_VERSION;$PROTOCOL_VERSION" # version, min 169 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
161 . ";$AnyEvent::MP::Base::UNIQ" 170 . ";$AnyEvent::MP::Base::UNIQ"
162 . ";$AnyEvent::MP::Base::NODE" 171 . ";$AnyEvent::MP::Base::NODE"
163 . ";" . (join ",", @AUTH_RCV) 172 . ";" . (join ",", @AUTH_RCV)
164 . ";" . (join ",", @FRAMINGS) 173 . ";" . (join ",", @FRAMINGS)
165 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv); 174 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv);
175
166 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), ""; 176 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
167 177
168 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012"); 178 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012");
169 179
170 # expect greeting 180 # expect greeting
181 $self->{hdl}->rbuf_max (4 * 1024);
171 $self->{hdl}->push_read (line => sub { 182 $self->{hdl}->push_read (line => sub {
172 my $rgreeting1 = $_[1]; 183 my $rgreeting1 = $_[1];
173 184
174 my ($aemp, $version, $version_min, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1; 185 my ($aemp, $version, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1;
175 186
176 if ($aemp ne "aemp") { 187 if ($aemp ne "aemp") {
177 return $self->error ("unparsable greeting"); 188 return $self->error ("unparsable greeting");
178 } elsif ($version_min > $PROTOCOL_VERSION) { 189 } elsif ($version != $PROTOCOL_VERSION) {
179 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version_min .. $version)"); 190 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version)");
180 } 191 }
181 192
182 my $s_auth; 193 my $s_auth;
183 for my $auth_ (split /,/, $auths) { 194 for my $auth_ (split /,/, $auths) {
184 if (grep $auth_ eq $_, @AUTH_SND) { 195 if (grep $auth_ eq $_, @AUTH_SND) {
213 224
214 # read nonce 225 # read nonce
215 $self->{hdl}->push_read (line => sub { 226 $self->{hdl}->push_read (line => sub {
216 my $rgreeting2 = $_[1]; 227 my $rgreeting2 = $_[1];
217 228
229 "$lgreeting1\012$lgreeting2" ne "$rgreeting1\012$rgreeting2" # echo attack?
230 or return $self->error ("authentication error, echo attack?");
231
232 my $key = Digest::MD6::md6 $secret;
233 my $lauth;
234
218 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{"tls"}) { 235 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
219 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept"; 236 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
220 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx}); 237 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
238 $s_auth = "tls";
239 $lauth = "";
240 } else {
241 # we currently only support hmac_md6_64_256
242 $lauth = Digest::HMAC_MD6::hmac_md6_hex $key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256;
221 } 243 }
222
223 # auth
224 require Digest::MD6;
225 require Digest::HMAC_MD6;
226
227 my $key = Digest::MD6::md6_hex ($secret);
228 my $lauth = Digest::HMAC_MD6::hmac_md6_base64 ($key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256);
229
230 my $rauth =
231 $s_auth eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_base64 ($key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256)
232 : $s_auth eq "hex_secret" ? unpack "H*", $secret
233 : die;
234
235 $lauth ne $rauth # echo attack?
236 or return $self->error ("authentication error");
237 244
238 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012"); 245 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
239 246
240 $self->{hdl}->rbuf_max (64); # enough for 44 reply bytes or so 247 # read the authentication response
241 $self->{hdl}->push_read (line => sub { 248 $self->{hdl}->push_read (line => sub {
242 my ($hdl, $rline) = @_; 249 my ($hdl, $rline) = @_;
243 250
244 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline; 251 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline;
252
253 my $rauth =
254 $auth_method eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex $key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256
255 : $auth_method eq "cleartext" ? unpack "H*", $secret
256 : $auth_method eq "tls" ? ($self->{tls} ? "" : "\012\012") # \012\012 never matches
257 : return $self->error ("$auth_method: fatal, selected unsupported auth method");
245 258
246 if ($rauth2 ne $rauth) { 259 if ($rauth2 ne $rauth) {
247 return $self->error ("authentication failure/shared secret mismatch"); 260 return $self->error ("authentication failure/shared secret mismatch");
248 } 261 }
249 262
252 $hdl->rbuf_max (undef); 265 $hdl->rbuf_max (undef);
253 my $queue = delete $self->{queue}; # we are connected 266 my $queue = delete $self->{queue}; # we are connected
254 267
255 $self->connected; 268 $self->connected;
256 269
270 my $src_node = $self->{node};
271
257 $hdl->push_write ($self->{s_framing} => $_) 272 $hdl->push_write ($self->{s_framing} => $_)
258 for @$queue; 273 for @$queue;
259 274
260 my $rmsg; $rmsg = sub { 275 my $rmsg; $rmsg = sub {
261 $_[0]->push_read ($r_framing => $rmsg); 276 $_[0]->push_read ($r_framing => $rmsg);
262 277
278 local $AnyEvent::MP::Base::SRCNODE = $src_node;
263 AnyEvent::MP::Base::_inject ($_[1]); 279 AnyEvent::MP::Base::_inject (@{ $_[1] });
264 }; 280 };
265 $hdl->push_read ($r_framing => $rmsg); 281 $hdl->push_read ($r_framing => $rmsg);
266 }); 282 });
267 }); 283 });
268 }); 284 });
273 289
274sub error { 290sub error {
275 my ($self, $msg) = @_; 291 my ($self, $msg) = @_;
276 292
277 if ($self->{node} && $self->{node}{transport} == $self) { 293 if ($self->{node} && $self->{node}{transport} == $self) {
294 $self->{node}->fail (transport_error => $msg);
278 $self->{node}->clr_transport; 295 $self->{node}->clr_transport;
279 } 296 }
280 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg"); 297 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
281 $self->destroy; 298 $self->destroy;
282} 299}
319The greeting consists of two text lines that are ended by either an ASCII 336The greeting consists of two text lines that are ended by either an ASCII
320CR LF pair, or a single ASCII LF (recommended). 337CR LF pair, or a single ASCII LF (recommended).
321 338
322=head2 GREETING 339=head2 GREETING
323 340
341All the lines until after authentication must not exceed 4kb in length,
342including delimiter. Afterwards there is no limit on the packet size that
343can be received.
344
345=head3 First Greeting Line
346
347Example:
348
349 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0
350
324The first line contains strings separated (not ended) by C<;> 351The first line contains strings separated (not ended) by C<;>
325characters. The first seven strings are fixed by the protocol, the 352characters. The first even ixtrings are fixed by the protocol, the
326remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;> 353remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;>
327characters themselves. 354characters themselves.
328 355
329The seven fixed strings are: 356The fixed strings are:
330 357
331=over 4 358=over 4
332 359
333=item C<aemp> 360=item protocol identification
334 361
335The constant C<aemp> to identify the protocol. 362The constant C<aemp> to identify the protocol.
336 363
337=item protocol version 364=item protocol version
338 365
339The (maximum) protocol version supported by this end, currently C<0>.
340
341=item minimum protocol version
342
343The minimum protocol version supported by this end, currently C<0>. 366The protocol version supported by this end, currently C<0>. If the
367versions don't match then no communication is possible. Minor extensions
368are supposed to be handled through additional key-value pairs.
344 369
345=item a token uniquely identifying the current node instance 370=item a token uniquely identifying the current node instance
346 371
347This is a string that must change between restarts. It usually contains 372This is a string that must change between restarts. It usually contains
348things like the current time, the (OS) process id or similar values, but 373things like the current time, the (OS) process id or similar values, but
366 391
367A comma-separated list of packet encoding/framign formats understood. The 392A comma-separated list of packet encoding/framign formats understood. The
368receiving side should choose the first framing format it supports for 393receiving side should choose the first framing format it supports for
369sending packets (which might be different from the format it has to accept). 394sending packets (which might be different from the format it has to accept).
370 395
371=cut 396=back
372 397
373The remaining arguments are C<KEY=VALUE> pairs. The following key-value 398The remaining arguments are C<KEY=VALUE> pairs. The following key-value
374pairs are known at this time: 399pairs are known at this time:
375 400
376=over 4 401=over 4
389 414
390Indicates that the other side supports TLS (version should be 1.0) and 415Indicates that the other side supports TLS (version should be 1.0) and
391wishes to do a TLS handshake. 416wishes to do a TLS handshake.
392 417
393=back 418=back
419
420=head3 Second Greeting Line
394 421
395After this greeting line there will be a second line containing a 422After this greeting line there will be a second line containing a
396cryptographic nonce, i.e. random data of high quality. To keep the 423cryptographic nonce, i.e. random data of high quality. To keep the
397protocol text-only, these are usually 32 base64-encoded octets, but 424protocol text-only, these are usually 32 base64-encoded octets, but
398it could be anything that doesn't contain any ASCII CR or ASCII LF 425it could be anything that doesn't contain any ASCII CR or ASCII LF
399characters. 426characters.
400 427
401Example of the two lines of greeting: 428I<< The two nonces B<must> be different, and an aemp implementation
429B<must> check and fail when they are identical >>.
402 430
403 aemp;0;0;e7d.4a76f48f;10.0.0.1:4040;hmac_md6_64_256,hex_secret;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:1235 431Example of a nonce line:
404 XntegV2Guvss0qNn7phCPnoU87xqxV+4Mqm/5y4iQm6a 432
433 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
405 434
406=head2 TLS handshake 435=head2 TLS handshake
407 436
408If, after the handshake, both sides indicate interest in TLS, then the 437I<< If, after the handshake, both sides indicate interest in TLS, then the
409connection I<must> use TLS, or fail. 438connection B<must> use TLS, or fail.>>
410 439
411Both sides compare their nonces, and the side who sent the lower nonce 440Both sides compare their nonces, and the side who sent the lower nonce
412value ("string" comparison on the raw octet values) becomes the client, 441value ("string" comparison on the raw octet values) becomes the client,
413and the one with the higher nonce the server. 442and the one with the higher nonce the server.
414 443
425 454
426=item the authentication method chosen 455=item the authentication method chosen
427 456
428This must be one of the methods offered by the other side in the greeting. 457This must be one of the methods offered by the other side in the greeting.
429 458
459The currently supported authentication methods are:
460
461=over 4
462
463=item cleartext
464
465This is simply the shared secret, lowercase-hex-encoded. This method is of
466course very insecure, unless TLS is used, which is why this module will
467accept, but not generate, cleartext auth replies.
468
469=item hmac_md6_64_256
470
471This method uses an MD6 HMAC with 64 bit blocksize and 256 bit hash. First, the shared secret
472is hashed with MD6:
473
474 key = MD6 (secret)
475
476This secret is then used to generate the "local auth reply", by taking
477the two local greeting lines and the two remote greeting lines (without
478line endings), appending \012 to all of them, concatenating them and
479calculating the MD6 HMAC with the key.
480
481 lauth = HMAC_MD6 key, "lgreeting1\012lgreeting2\012rgreeting1\012rgreeting2\012"
482
483This authentication token is then lowercase-hex-encoded and sent to the
484other side.
485
486Then the remote auth reply is generated using the same method, but local
487and remote greeting lines swapped:
488
489 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
490
491This is the token that is expected from the other side.
492
493=item tls
494
495This type is only valid iff TLS was enabled and the TLS handshake
496was successful. It has no authentication data, as the server/client
497certificate was successfully verified.
498
499Implementations supporting TLS I<must> accept this authentication type.
500
501=back
502
430=item the authentication data 503=item the authentication data
431 504
432The authentication data itself, usually base64 or hex-encoded data. 505The authentication data itself, usually base64 or hex-encoded data, see
506above.
433 507
434=item the framing protocol chosen 508=item the framing protocol chosen
435 509
436This must be one of the framing protocols offered by the other side in the 510This must be one of the framing protocols offered by the other side in the
437greeting. Each side must accept the choice of the other side. 511greeting. Each side must accept the choice of the other side.
438 512
439=back 513=back
440 514
515Example of an authentication reply:
516
517 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
518
441=head2 DATA PHASE 519=head2 DATA PHASE
442 520
443After this, packets get exchanged using the chosen framing protocol. It is 521After this, packets get exchanged using the chosen framing protocol. It is
444quite possible that both sides use a different framing protocol. 522quite possible that both sides use a different framing protocol.
445 523
524=head2 FULL EXAMPLE
525
526This is an actual protocol dump of a handshake, followed by a single data
527packet. The greater than/less than lines indicate the direction of the
528transfer only.
529
530 > aemp;0;nndKd+gn;10.0.0.1:4040;hmac_md6_64_256,cleartext;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:1235
531 > sRG8bbc4TDbkpvH8FTP4HBs87OhepH6VuApoZqXXskuG
532 < aemp;0;nmpKd+gh;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256,cleartext;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:58760
533 < dCEUcL/LJVSTJcx8byEsOzrwhzJYOq+L3YcopA5T6EAo
534 > hmac_md6_64_256;9513d4b258975accfcb2ab7532b83690e9c119a502c612203332a591c7237788;json
535 < hmac_md6_64_256;0298d6ba2240faabb2b2e881cf86b97d70a113ca74a87dc006f9f1e9d3010f90;json
536 > ["","lookup","pinger","10.0.0.1:4040#nndKd+gn.a","resolved"]
537
446=head1 SEE ALSO 538=head1 SEE ALSO
447 539
448L<AnyEvent>. 540L<AnyEvent>.
449 541
450=head1 AUTHOR 542=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines