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.11 by root, Mon Aug 3 15:40:53 2009 UTC vs.
Revision 1.23 by root, Wed Aug 5 22:40:51 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
26 26
27use Scalar::Util; 27use Scalar::Util;
28use MIME::Base64 (); 28use MIME::Base64 ();
29use Storable (); 29use Storable ();
30use JSON::XS (); 30use JSON::XS ();
31
32use Digest::MD6 ();
33use Digest::HMAC_MD6 ();
31 34
32use AE (); 35use AE ();
33use AnyEvent::Socket (); 36use AnyEvent::Socket ();
34use AnyEvent::Handle (); 37use AnyEvent::Handle ();
35 38
111 114
112=cut 115=cut
113 116
114our @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
115our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send 118our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send
116our @AUTH_RCV = (@AUTH_SND, qw(hex_secret)); # auth types we accept 119our @AUTH_RCV = (@AUTH_SND, qw(cleartext)); # auth types we accept
117 120
118#AnyEvent::Handle::register_write_type mp_record => sub { 121#AnyEvent::Handle::register_write_type mp_record => sub {
119#}; 122#};
120 123
121sub new { 124sub new {
126 $self->{queue} = []; 129 $self->{queue} = [];
127 130
128 { 131 {
129 Scalar::Util::weaken (my $self = $self); 132 Scalar::Util::weaken (my $self = $self);
130 133
131 $arg{tls_ctx_disabled} ||= {
132 sslv2 => 0,
133 sslv3 => 0,
134 tlsv1 => 1,
135 verify => 1,
136 cert_file => "secret.pem",
137 ca_file => "secret.pem",
138 verify_require_client_cert => 1,
139 };
140
141 $arg{secret} = AnyEvent::MP::Base::default_secret () 134 $arg{secret} = AnyEvent::MP::Base::default_secret ()
142 unless exists $arg{secret}; 135 unless exists $arg{secret};
143 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
144 $self->{hdl} = new AnyEvent::Handle 152 $self->{hdl} = new AnyEvent::Handle
145 fh => delete $arg{fh}, 153 fh => delete $arg{fh},
146 rbuf_max => 64 * 1024,
147 autocork => 1, 154 autocork => 1,
148 no_delay => 1, 155 no_delay => 1,
149 on_error => sub { 156 on_error => sub {
150 $self->error ($_[2]); 157 $self->error ($_[2]);
151 }, 158 },
152 peername => delete $arg{peername}, 159 peername => delete $arg{peername},
153 ; 160 ;
154 161
155 my $secret = $arg{secret};
156 my $greeting_kv = $self->{greeting} ||= {}; 162 my $greeting_kv = $self->{greeting} ||= {};
157 $greeting_kv->{"tls"} = "1.0" 163 $greeting_kv->{"tls"} = "1.0"
158 if $arg{tls_ctx}; 164 if $arg{tls_ctx};
159 $greeting_kv->{provider} = "AE-$VERSION"; 165 $greeting_kv->{provider} = "AE-$VERSION";
160 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport}; 166 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport};
161 167
168 $self->{local_node} = $AnyEvent::MP::Base::NODE;
169
162 # send greeting 170 # send greeting
163 my $lgreeting1 = "aemp;$PROTOCOL_VERSION;$PROTOCOL_VERSION" # version, min 171 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
164 . ";$AnyEvent::MP::Base::UNIQ" 172 . ";$AnyEvent::MP::Base::UNIQ"
165 . ";$AnyEvent::MP::Base::NODE" 173 . ";$AnyEvent::MP::Base::NODE"
166 . ";" . (join ",", @AUTH_RCV) 174 . ";" . (join ",", @AUTH_RCV)
167 . ";" . (join ",", @FRAMINGS) 175 . ";" . (join ",", @FRAMINGS)
168 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv); 176 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv);
177
169 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), ""; 178 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
170 179
171 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012"); 180 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012");
172 181
173 # expect greeting 182 # expect greeting
183 $self->{hdl}->rbuf_max (4 * 1024);
174 $self->{hdl}->push_read (line => sub { 184 $self->{hdl}->push_read (line => sub {
175 my $rgreeting1 = $_[1]; 185 my $rgreeting1 = $_[1];
176 186
177 my ($aemp, $version, $version_min, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1; 187 my ($aemp, $version, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1;
178 188
179 if ($aemp ne "aemp") { 189 if ($aemp ne "aemp") {
180 return $self->error ("unparsable greeting"); 190 return $self->error ("unparsable greeting");
181 } elsif ($version_min > $PROTOCOL_VERSION) { 191 } elsif ($version != $PROTOCOL_VERSION) {
182 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version_min .. $version)"); 192 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version)");
183 } 193 }
184 194
185 my $s_auth; 195 my $s_auth;
186 for my $auth_ (split /,/, $auths) { 196 for my $auth_ (split /,/, $auths) {
187 if (grep $auth_ eq $_, @AUTH_SND) { 197 if (grep $auth_ eq $_, @AUTH_SND) {
216 226
217 # read nonce 227 # read nonce
218 $self->{hdl}->push_read (line => sub { 228 $self->{hdl}->push_read (line => sub {
219 my $rgreeting2 = $_[1]; 229 my $rgreeting2 = $_[1];
220 230
231 "$lgreeting1\012$lgreeting2" ne "$rgreeting1\012$rgreeting2" # echo attack?
232 or return $self->error ("authentication error, echo attack?");
233
234 my $key = Digest::MD6::md6 $secret;
235 my $lauth;
236
221 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) { 237 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
222 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept"; 238 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
223 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx}); 239 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
240 $s_auth = "tls";
241 $lauth = "";
242 } else {
243 # we currently only support hmac_md6_64_256
244 $lauth = Digest::HMAC_MD6::hmac_md6_hex $key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256;
224 } 245 }
225
226 # auth
227 require Digest::MD6;
228 require Digest::HMAC_MD6;
229
230 my $key = Digest::MD6::md6 ($secret);
231 my $lauth = Digest::HMAC_MD6::hmac_md6_base64 ($key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256);
232
233 my $rauth =
234 $s_auth eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_base64 ($key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256)
235 : $s_auth eq "hex_secret" ? unpack "H*", $secret
236 : die;
237
238 $lauth ne $rauth # echo attack?
239 or return $self->error ("authentication error");
240 246
241 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012"); 247 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
242 248
243 $self->{hdl}->rbuf_max (64); # enough for 44 reply bytes or so 249 # read the authentication response
244 $self->{hdl}->push_read (line => sub { 250 $self->{hdl}->push_read (line => sub {
245 my ($hdl, $rline) = @_; 251 my ($hdl, $rline) = @_;
246 252
247 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline; 253 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline;
254
255 my $rauth =
256 $auth_method eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex $key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256
257 : $auth_method eq "cleartext" ? unpack "H*", $secret
258 : $auth_method eq "tls" ? ($self->{tls} ? "" : "\012\012") # \012\012 never matches
259 : return $self->error ("$auth_method: fatal, selected unsupported auth method");
248 260
249 if ($rauth2 ne $rauth) { 261 if ($rauth2 ne $rauth) {
250 return $self->error ("authentication failure/shared secret mismatch"); 262 return $self->error ("authentication failure/shared secret mismatch");
251 } 263 }
252 264
255 $hdl->rbuf_max (undef); 267 $hdl->rbuf_max (undef);
256 my $queue = delete $self->{queue}; # we are connected 268 my $queue = delete $self->{queue}; # we are connected
257 269
258 $self->connected; 270 $self->connected;
259 271
260 $hdl->push_write ($self->{s_framing} => $_) 272 my $src_node = $self->{node};
273
274 $self->send ($_)
261 for @$queue; 275 for @$queue;
262 276
263 my $rmsg; $rmsg = sub { 277 my $rmsg; $rmsg = sub {
264 $_[0]->push_read ($r_framing => $rmsg); 278 $_[0]->push_read ($r_framing => $rmsg);
265 279
280 local $AnyEvent::MP::Base::SRCNODE = $src_node;
266 AnyEvent::MP::Base::_inject ($_[1]); 281 AnyEvent::MP::Base::_inject (@{ $_[1] });
267 }; 282 };
268 $hdl->push_read ($r_framing => $rmsg); 283 $hdl->push_read ($r_framing => $rmsg);
269 }); 284 });
270 }); 285 });
271 }); 286 });
276 291
277sub error { 292sub error {
278 my ($self, $msg) = @_; 293 my ($self, $msg) = @_;
279 294
280 if ($self->{node} && $self->{node}{transport} == $self) { 295 if ($self->{node} && $self->{node}{transport} == $self) {
296 #TODO: store error, but do not instantly fail
297 $self->{node}->fail (transport_error => $self->{node}{noderef}, $msg);
281 $self->{node}->clr_transport; 298 $self->{node}->clr_transport;
282 } 299 }
283 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg"); 300 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
284 $self->destroy; 301 $self->destroy;
285} 302}
286 303
287sub connected { 304sub connected {
288 my ($self) = @_; 305 my ($self) = @_;
289 306
307 if (ref $AnyEvent::MP::Base::SLAVE) {
308 # first connect with a master node
309 $AnyEvent::MP::Base::NODE .= "\@$self->{remote_node}";
310 $AnyEvent::MP::Base::NODE{$AnyEvent::MP::Base::NODE} = $AnyEvent::MP::Base::NODE{""};
311 $AnyEvent::MP::Base::SLAVE->();
312 }
313
314 if ($self->{local_node} ne $AnyEvent::MP::Base::NODE) {
315 # node changed its name since first greeting
316 $self->send (["", iam => $AnyEvent::MP::Base::NODE]);
317 }
318
290 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node}); 319 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node});
291 Scalar::Util::weaken ($self->{node} = $node); 320 Scalar::Util::weaken ($self->{node} = $node);
292 $node->set_transport ($self); 321 $node->set_transport ($self);
293} 322}
294 323
322The greeting consists of two text lines that are ended by either an ASCII 351The greeting consists of two text lines that are ended by either an ASCII
323CR LF pair, or a single ASCII LF (recommended). 352CR LF pair, or a single ASCII LF (recommended).
324 353
325=head2 GREETING 354=head2 GREETING
326 355
356All the lines until after authentication must not exceed 4kb in length,
357including delimiter. Afterwards there is no limit on the packet size that
358can be received.
359
360=head3 First Greeting Line
361
362Example:
363
364 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0
365
327The first line contains strings separated (not ended) by C<;> 366The first line contains strings separated (not ended) by C<;>
328characters. The first seven strings are fixed by the protocol, the 367characters. The first even ixtrings are fixed by the protocol, the
329remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;> 368remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;>
330characters themselves. 369characters themselves.
331 370
332The seven fixed strings are: 371The fixed strings are:
333 372
334=over 4 373=over 4
335 374
336=item C<aemp> 375=item protocol identification
337 376
338The constant C<aemp> to identify the protocol. 377The constant C<aemp> to identify the protocol.
339 378
340=item protocol version 379=item protocol version
341 380
342The (maximum) protocol version supported by this end, currently C<0>.
343
344=item minimum protocol version
345
346The minimum protocol version supported by this end, currently C<0>. 381The protocol version supported by this end, currently C<0>. If the
382versions don't match then no communication is possible. Minor extensions
383are supposed to be handled through additional key-value pairs.
347 384
348=item a token uniquely identifying the current node instance 385=item a token uniquely identifying the current node instance
349 386
350This is a string that must change between restarts. It usually contains 387This is a string that must change between restarts. It usually contains
351things like the current time, the (OS) process id or similar values, but 388things like the current time, the (OS) process id or similar values, but
392 429
393Indicates that the other side supports TLS (version should be 1.0) and 430Indicates that the other side supports TLS (version should be 1.0) and
394wishes to do a TLS handshake. 431wishes to do a TLS handshake.
395 432
396=back 433=back
434
435=head3 Second Greeting Line
397 436
398After this greeting line there will be a second line containing a 437After this greeting line there will be a second line containing a
399cryptographic nonce, i.e. random data of high quality. To keep the 438cryptographic nonce, i.e. random data of high quality. To keep the
400protocol text-only, these are usually 32 base64-encoded octets, but 439protocol text-only, these are usually 32 base64-encoded octets, but
401it could be anything that doesn't contain any ASCII CR or ASCII LF 440it could be anything that doesn't contain any ASCII CR or ASCII LF
402characters. 441characters.
403 442
404Example of the two lines of greeting: 443I<< The two nonces B<must> be different, and an aemp implementation
444B<must> check and fail when they are identical >>.
405 445
406 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 446Example of a nonce line:
407 XntegV2Guvss0qNn7phCPnoU87xqxV+4Mqm/5y4iQm6a 447
448 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
408 449
409=head2 TLS handshake 450=head2 TLS handshake
410 451
411If, after the handshake, both sides indicate interest in TLS, then the 452I<< If, after the handshake, both sides indicate interest in TLS, then the
412connection I<must> use TLS, or fail. 453connection B<must> use TLS, or fail. >>
413 454
414Both sides compare their nonces, and the side who sent the lower nonce 455Both sides compare their nonces, and the side who sent the lower nonce
415value ("string" comparison on the raw octet values) becomes the client, 456value ("string" comparison on the raw octet values) becomes the client,
416and the one with the higher nonce the server. 457and the one with the higher nonce the server.
417 458
428 469
429=item the authentication method chosen 470=item the authentication method chosen
430 471
431This must be one of the methods offered by the other side in the greeting. 472This must be one of the methods offered by the other side in the greeting.
432 473
474The currently supported authentication methods are:
475
476=over 4
477
478=item cleartext
479
480This is simply the shared secret, lowercase-hex-encoded. This method is of
481course very insecure, unless TLS is used, which is why this module will
482accept, but not generate, cleartext auth replies.
483
484=item hmac_md6_64_256
485
486This method uses an MD6 HMAC with 64 bit blocksize and 256 bit hash. First, the shared secret
487is hashed with MD6:
488
489 key = MD6 (secret)
490
491This secret is then used to generate the "local auth reply", by taking
492the two local greeting lines and the two remote greeting lines (without
493line endings), appending \012 to all of them, concatenating them and
494calculating the MD6 HMAC with the key.
495
496 lauth = HMAC_MD6 key, "lgreeting1\012lgreeting2\012rgreeting1\012rgreeting2\012"
497
498This authentication token is then lowercase-hex-encoded and sent to the
499other side.
500
501Then the remote auth reply is generated using the same method, but local
502and remote greeting lines swapped:
503
504 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
505
506This is the token that is expected from the other side.
507
508=item tls
509
510This type is only valid iff TLS was enabled and the TLS handshake
511was successful. It has no authentication data, as the server/client
512certificate was successfully verified.
513
514Implementations supporting TLS I<must> accept this authentication type.
515
516=back
517
433=item the authentication data 518=item the authentication data
434 519
435The authentication data itself, usually base64 or hex-encoded data. 520The authentication data itself, usually base64 or hex-encoded data, see
521above.
436 522
437=item the framing protocol chosen 523=item the framing protocol chosen
438 524
439This must be one of the framing protocols offered by the other side in the 525This must be one of the framing protocols offered by the other side in the
440greeting. Each side must accept the choice of the other side. 526greeting. Each side must accept the choice of the other side.
441 527
442=back 528=back
443 529
444Example (the actual reply matching the previous example): 530Example of an authentication reply:
445 531
446 hmac_md6_64_256;wIlLedBY956UCGSISG9mBZRDTG8xUi73/sVse2DSQp0;json 532 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
447 533
448=head2 DATA PHASE 534=head2 DATA PHASE
449 535
450After this, packets get exchanged using the chosen framing protocol. It is 536After this, packets get exchanged using the chosen framing protocol. It is
451quite possible that both sides use a different framing protocol. 537quite possible that both sides use a different framing protocol.
452 538
539=head2 FULL EXAMPLE
540
541This is an actual protocol dump of a handshake, followed by a single data
542packet. The greater than/less than lines indicate the direction of the
543transfer only.
544
545 > 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
546 > sRG8bbc4TDbkpvH8FTP4HBs87OhepH6VuApoZqXXskuG
547 < 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
548 < dCEUcL/LJVSTJcx8byEsOzrwhzJYOq+L3YcopA5T6EAo
549 > hmac_md6_64_256;9513d4b258975accfcb2ab7532b83690e9c119a502c612203332a591c7237788;json
550 < hmac_md6_64_256;0298d6ba2240faabb2b2e881cf86b97d70a113ca74a87dc006f9f1e9d3010f90;json
551 > ["","lookup","pinger","10.0.0.1:4040#nndKd+gn.a","resolved"]
552
453=head1 SEE ALSO 553=head1 SEE ALSO
454 554
455L<AnyEvent>. 555L<AnyEvent>.
456 556
457=head1 AUTHOR 557=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines