Changeset 216
- Timestamp:
- 09/26/06 23:44:38 (2 years ago)
- Files:
-
- trunk/lib/AxKit2/Client.pm (modified) (2 diffs)
- trunk/lib/AxKit2/HTTPHeaders.pm (modified) (1 diff)
- trunk/lib/AxKit2/Plugin.pm (modified) (3 diffs)
- trunk/plugins/authenticate (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/lib/AxKit2/Client.pm
r205 r216 100 100 my ($self, $hook) = (shift, shift); 101 101 102 croak "Recursive call: Hook $hook already in progress. Args: ".join(", ",@_) if $current_hook && $current_hook eq $hook ;102 croak "Recursive call: Hook $hook already in progress. Args: ".join(", ",@_) if $current_hook && $current_hook eq $hook && $hook ne 'message'; 103 103 local $current_hook = $hook; 104 104 … … 178 178 } 179 179 180 sub send { 181 my $self = shift; 182 my @rc = $self->run_hooks('message', @_); 183 $self->log(LOGWARN,"Unhandled message: $_[0]") if shift(@rc) != OK; 184 return @_ if wantarray; 185 return $_[0]; 186 } 187 180 188 sub hook_connect { 181 189 my $self = shift; trunk/lib/AxKit2/HTTPHeaders.pm
r215 r216 543 543 } 544 544 545 =head2 C<< $obj->header_remove( KEY ) >> 546 547 Remove a given header. 548 549 =cut 550 sub header_remove { 551 my AxKit2::HTTPHeaders $self = shift; 552 my $key = lc(shift); 553 554 delete $self->{headers}{$key}; 555 @{$self->{hdorder}} = grep { $_ ne $key } @{$self->{hdorder}}; 556 delete $self->{origcase}{$key}; 557 } 558 545 559 =head2 C<< $obj->lame_request >> 546 560 trunk/lib/AxKit2/Plugin.pm
r214 r216 29 29 logging connect pre_request post_read_request body_data uri_translation 30 30 mime_map access_control authentication authorization fixup write_body_data 31 xmlresponse response response_sent disconnect error 31 xmlresponse response response_sent disconnect error message 32 32 ); 33 33 our %hooks = map { $_ => 1 } @hooks; … … 80 80 } 81 81 82 sub send { 83 my $self = shift; 84 $self->client->send(@_); 85 } 86 87 sub notes { 88 my $self = shift; 89 my $name = shift; 90 $self->client->notes($self->plugin_name.'::'.$name,@_); 91 } 92 82 93 sub log { 83 94 my $self = shift; … … 100 111 $self->register_hook( $hook, $hooksub ) if ($self->can($hooksub)); 101 112 } 113 114 my $package = ref($self); 115 no strict 'refs'; 116 foreach my $key (keys %{*{$package."::"}}) { 117 next unless $key =~ m/^message_/ && $package->can($key); 118 $self->register_hook('message','dispatch_message'); 119 last; 120 } 121 } 122 123 sub dispatch_message { 124 my $self = shift; 125 my $message = shift; 126 if (my $sub = $self->can("message_$message")) { 127 return OK, $sub->($self,@_); 128 } 129 return DECLINED; 102 130 } 103 131 trunk/plugins/authenticate
r215 r216 172 172 for different hosts in that list. Default value is "/", i.e., the whole server. 173 173 174 =head1 API174 =head1 MESSAGES HANDLED 175 175 176 176 =cut … … 207 207 } 208 208 209 sub hook_body_data { 210 my ($self, $bref) = @_; 211 212 $self->notes('bodyhash', Digest::MD5->new) if !$self->notes('bodyhash'); 213 my $ctx = $self->notes('bodyhash'); 214 $ctx->add($$bref); 215 216 return DECLINED; 217 } 218 209 219 my $_SERVER_SECRET = rand().rand(); # FIXME: get some kind of cryptographically secure randomness 210 220 my %_NC; … … 215 225 pages will result in a new login prompt. 216 226 227 =head1 MESSAGES SENT 228 229 =head2 C<< login >> 230 231 This message is sent when a user successfully starts a new login session. 232 233 =head2 C<< login_failed >> 234 235 This message is sent whenever a real login failure is detected. Your application code 236 might want to lock out users after a certain number of failed attempts. 237 238 =head2 C<< login_timeout >> 239 240 This message is sent whenever a login session times out. This can happen I<after> the user 241 has logged in with another session, but it will sooner or later be sent for every session 242 not logged out via message C<logout>. 243 217 244 =cut 218 245 219 sub logout {246 sub message_logout { 220 247 my ($self) = @_; 221 248 my $session = $self->client->headers_in->header('Digest-Session'); … … 240 267 $self->log(LOGINFO,"Sent header [$header]"); 241 268 $self->client->headers_out->header("WWW-Authenticate",$header); 269 $self->client->headers_out->header_remove('Authentication-Info'); 242 270 243 271 return UNAUTHORIZED; … … 247 275 my ($self, $msg) = @_; 248 276 $self->log(LOGWARN,"Invalid access! Possible hacking attempt. $msg"); 277 $self->client->headers_out->header_remove('Authentication-Info'); 278 249 279 return FORBIDDEN; 250 280 } … … 278 308 return OK if ($hash =~ m/^{SHA}(.{27})/ && sha1_base64($pass) eq $1); 279 309 return OK if (crypt($pass,$hash) eq $hash); 310 $self->send('login_failed',$user); 280 311 } 281 312 … … 294 325 $value =~ s/\\(.)/$1/g; 295 326 } 296 warn("Param: $param, Value: $value\n");297 327 $param{lc($param)} = $value; 298 328 } … … 318 348 return $self->send_digest_forbidden("impossible nonce") if (int($timestamp) ne $timestamp || !defined $nonce || int($timestamp) > time()); 319 349 320 my $bodyhash ; # TODO350 my $bodyhash = ($self->notes('bodyhash')? $self->notes('bodyhash')->hexdigest : '0' x 32); 321 351 my $A2 = md5_hex( $hd->request_method . ':' . $param{uri} . ($param{qop} eq 'auth-int'? ':' . $bodyhash : '') ); 322 352 my $response = md5_hex( $hash->[0] . ':' . encode_base64($param{nonce},"") . ':' . 323 353 ($param{qop}? $param{nc} . ':' . $param{cnonce} . ':' . $param{qop} . ':' : '') . 324 354 $A2 ); 325 # Wrong password, or worse. TODO: add a "failed password" callback 326 return $self->send_digest_challenge if ($response ne $param{response}); 327 328 my $nonce = $timestamp.':'.$session.':'.md5($timestamp.':'.$session.':'.$_SERVER_SECRET); 355 # Wrong password, or worse. 356 $self->send('login_failed',$param{username}), return $self->send_digest_challenge if ($response ne $param{response}); 357 358 $A2 = md5_hex( ':' . $param{uri} ); # TODO: provide auth-int? . ($param{qop} eq 'auth-int'? ':' . $bodyhash : '') 359 $response = md5_hex( $hash->[0] . ':' . encode_base64($param{nonce},"") . ':' . 360 $param{nc} . ':' . $param{cnonce} . ':' . 'auth' . ':' . 361 $A2 ); 362 $self->client->headers_out->header('Authentication-Info',"qop=auth, cnonce=".quoted_string($param{cnonce}).", nc=$param{nc}, rspauth=".quoted_string($response)); 363 364 $nonce = $timestamp.':'.$session.':'.md5($timestamp.':'.$session.':'.$_SERVER_SECRET); 329 365 # This could be a hacking attempt, or a server restart. 330 366 return $self->send_digest_challenge($param{nonce},$session) if ($param{nonce} ne $nonce); 331 332 367 333 368 $hd->header('Digest-Session',$session); … … 335 370 # Force relogin if the first challenge response takes too long (2 minutes). 336 371 return $self->send_digest_challenge if int($timestamp) < time()-120; 337 $_NC{ nonce} = 0;338 # TODO: add a 'successful login' callback372 $_NC{$nonce} = 0; 373 $self->send('login',$param{username}); 339 374 } 340 375 … … 351 386 return $self->send_digest_forbidden("replay attack") if ($param{nc} ne sprintf("%08x",++$_NC{$nonce})); 352 387 353 # TODO: add an "idle-time" callback354 355 388 # All checks passed. 356 389 return OK;
