Changeset 216

Show
Ignore:
Timestamp:
09/26/06 23:44:38 (2 years ago)
Author:
jwalt
Message:
  • implement qop=auth for HTTP digest
  • add generic message passing interface for plugins
  • add notes helper
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/lib/AxKit2/Client.pm

    r205 r216  
    100100    my ($self, $hook) = (shift, shift); 
    101101     
    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'
    103103    local $current_hook = $hook; 
    104104     
     
    178178} 
    179179 
     180sub 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 
    180188sub hook_connect { 
    181189    my $self = shift; 
  • trunk/lib/AxKit2/HTTPHeaders.pm

    r215 r216  
    543543} 
    544544 
     545=head2 C<< $obj->header_remove( KEY ) >> 
     546 
     547Remove a given header. 
     548 
     549=cut 
     550sub 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 
    545559=head2 C<< $obj->lame_request >> 
    546560 
  • trunk/lib/AxKit2/Plugin.pm

    r214 r216  
    2929    logging connect pre_request post_read_request body_data uri_translation 
    3030    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 
    3232); 
    3333our %hooks = map { $_ => 1 } @hooks; 
     
    8080} 
    8181 
     82sub send { 
     83    my $self = shift; 
     84    $self->client->send(@_); 
     85} 
     86 
     87sub notes { 
     88    my $self = shift; 
     89    my $name = shift; 
     90    $self->client->notes($self->plugin_name.'::'.$name,@_); 
     91} 
     92 
    8293sub log { 
    8394    my $self = shift; 
     
    100111        $self->register_hook( $hook, $hooksub ) if ($self->can($hooksub)); 
    101112    } 
     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 
     123sub 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; 
    102130} 
    103131 
  • trunk/plugins/authenticate

    r215 r216  
    172172for different hosts in that list. Default value is "/", i.e., the whole server. 
    173173 
    174 =head1 API 
     174=head1 MESSAGES HANDLED 
    175175 
    176176=cut 
     
    207207} 
    208208 
     209sub 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 
    209219my $_SERVER_SECRET = rand().rand(); # FIXME: get some kind of cryptographically secure randomness 
    210220my %_NC; 
     
    215225pages will result in a new login prompt. 
    216226 
     227=head1 MESSAGES SENT 
     228 
     229=head2 C<< login >> 
     230 
     231This message is sent when a user successfully starts a new login session. 
     232 
     233=head2 C<< login_failed >> 
     234 
     235This message is sent whenever a real login failure is detected. Your application code 
     236might want to lock out users after a certain number of failed attempts. 
     237 
     238=head2 C<< login_timeout >> 
     239 
     240This message is sent whenever a login session times out. This can happen I<after> the user 
     241has logged in with another session, but it will sooner or later be sent for every session 
     242not logged out via message C<logout>. 
     243 
    217244=cut 
    218245 
    219 sub logout { 
     246sub message_logout { 
    220247    my ($self) = @_; 
    221248    my $session = $self->client->headers_in->header('Digest-Session'); 
     
    240267    $self->log(LOGINFO,"Sent header [$header]"); 
    241268    $self->client->headers_out->header("WWW-Authenticate",$header); 
     269    $self->client->headers_out->header_remove('Authentication-Info'); 
    242270 
    243271    return UNAUTHORIZED; 
     
    247275    my ($self, $msg) = @_; 
    248276    $self->log(LOGWARN,"Invalid access! Possible hacking attempt. $msg"); 
     277    $self->client->headers_out->header_remove('Authentication-Info'); 
     278 
    249279    return FORBIDDEN; 
    250280} 
     
    278308            return OK if ($hash =~ m/^{SHA}(.{27})/ && sha1_base64($pass) eq $1); 
    279309            return OK if (crypt($pass,$hash) eq $hash); 
     310            $self->send('login_failed',$user); 
    280311        } 
    281312 
     
    294325                $value =~ s/\\(.)/$1/g; 
    295326            } 
    296             warn("Param: $param, Value: $value\n"); 
    297327            $param{lc($param)} = $value; 
    298328        } 
     
    318348        return $self->send_digest_forbidden("impossible nonce") if (int($timestamp) ne $timestamp || !defined $nonce || int($timestamp) > time()); 
    319349 
    320         my $bodyhash; # TODO 
     350        my $bodyhash = ($self->notes('bodyhash')? $self->notes('bodyhash')->hexdigest : '0' x 32); 
    321351        my $A2 = md5_hex( $hd->request_method . ':' . $param{uri} . ($param{qop} eq 'auth-int'? ':' . $bodyhash : '') ); 
    322352        my $response = md5_hex( $hash->[0] . ':' . encode_base64($param{nonce},"") . ':' . 
    323353                                ($param{qop}? $param{nc} . ':' . $param{cnonce} . ':' . $param{qop} . ':' : '') . 
    324354                                $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); 
    329365        # This could be a hacking attempt, or a server restart. 
    330366        return $self->send_digest_challenge($param{nonce},$session) if ($param{nonce} ne $nonce); 
    331  
    332367 
    333368        $hd->header('Digest-Session',$session); 
     
    335370            # Force relogin if the first challenge response takes too long (2 minutes). 
    336371            return $self->send_digest_challenge if int($timestamp) < time()-120; 
    337             $_NC{nonce} = 0; 
    338             # TODO: add a 'successful login' callback 
     372            $_NC{$nonce} = 0; 
     373            $self->send('login',$param{username}); 
    339374        } 
    340375 
     
    351386        return $self->send_digest_forbidden("replay attack") if ($param{nc} ne sprintf("%08x",++$_NC{$nonce})); 
    352387 
    353         # TODO: add an "idle-time" callback 
    354  
    355388        # All checks passed. 
    356389        return OK;