Show
Ignore:
Timestamp:
05/01/08 18:01:45 (5 years ago)
Author:
hirose31
Message:

クライアント認証してるhttps://にも対応した

そのため、OpenSSLを使わず、生でSSL喋るように実装しなおした。
というか、keepalived-extcheckのSSL_HELLOをまるぱくりです。id:yasui0906++
http://lab.klab.org/modules/mediawiki/index.php/Software#keepalived-extcheck

Location:
lang/perl/Net-SSL-ExpireDate/trunk
Files:
1 added
2 modified

Legend:

Unmodified
Added
Removed
  • lang/perl/Net-SSL-ExpireDate/trunk/Makefile.PL

    r10699 r10883  
    66requires 'UNIVERSAL::require'    => 0; 
    77requires 'Class::Accessor'       => 0; 
    8 requires 'IO::Socket::SSL'       => 0.99; 
    9 requires 'Net::SSLeay'           => 0; 
     8requires 'IO::Socket'            => 0; 
    109requires 'Crypt::OpenSSL::X509'  => 0; 
    1110requires 'Date::Parse'           => 0; 
  • lang/perl/Net-SSL-ExpireDate/trunk/lib/Net/SSL/ExpireDate.pm

    r10699 r10883  
    88 
    99use base qw(Class::Accessor); 
    10 use IO::Socket::SSL; 
    11 use Net::SSLeay; 
    12 use Crypt::OpenSSL::X509; 
     10use IO::Socket; 
     11use Crypt::OpenSSL::X509 qw(FORMAT_ASN1); 
    1312use Date::Parse; 
    1413use DateTime; 
     
    1817__PACKAGE__->mk_accessors(qw(type target)); 
    1918 
    20 BEGIN { 
    21     my $debug_flag = $ENV{SMART_COMMENTS} || $ENV{SMART_COMMENT} || $ENV{SMART_DEBUG} || $ENV{SC}; 
    22     if ($debug_flag) { 
    23         my @p = map { '#'x$_ } ($debug_flag =~ /([345])\s*/g); 
    24         use UNIVERSAL::require; 
    25         Smart::Comments->use(@p); 
    26     } 
    27 } 
     19my $SSL3_RT_CHANGE_CIPHER_SPEC = 20; 
     20my $SSL3_RT_ALERT              = 21; 
     21my $SSL3_RT_HANDSHAKE          = 22; 
     22my $SSL3_RT_APPLICATION_DATA   = 23; 
     23 
     24my $SSL3_MT_HELLO_REQUEST       =  0; 
     25my $SSL3_MT_CLIENT_HELLO        =  1; 
     26my $SSL3_MT_SERVER_HELLO        =  2; 
     27my $SSL3_MT_CERTIFICATE         = 11; 
     28my $SSL3_MT_SERVER_KEY_EXCHANGE = 12; 
     29my $SSL3_MT_CERTIFICATE_REQUEST = 13; 
     30my $SSL3_MT_SERVER_DONE         = 14; 
     31my $SSL3_MT_CERTIFICATE_VERIFY  = 15; 
     32my $SSL3_MT_CLIENT_KEY_EXCHANGE = 16; 
     33my $SSL3_MT_FINISHED            = 20; 
     34 
     35my $SSL3_AL_WARNING = 0x01; 
     36my $SSL3_AL_FATAL   = 0x02; 
     37 
     38my $SSL3_AD_CLOSE_NOTIFY            =  0; 
     39my $SSL3_AD_UNEXPECTED_MESSAGE      = 10; # fatal 
     40my $SSL3_AD_BAD_RECORD_MAC          = 20; # fatal 
     41my $SSL3_AD_DECOMPRESSION_FAILURE   = 30; # fatal 
     42my $SSL3_AD_HANDSHAKE_FAILURE       = 40; # fatal 
     43my $SSL3_AD_NO_CERTIFICATE          = 41; 
     44my $SSL3_AD_BAD_CERTIFICATE         = 42; 
     45my $SSL3_AD_UNSUPPORTED_CERTIFICATE = 43; 
     46my $SSL3_AD_CERTIFICATE_REVOKED     = 44; 
     47my $SSL3_AD_CERTIFICATE_EXPIRED     = 45; 
     48my $SSL3_AD_CERTIFICATE_UNKNOWN     = 46; 
     49my $SSL3_AD_ILLEGAL_PARAMETER       = 47; # fatal 
    2850 
    2951sub new { 
     
    6183            ### $host 
    6284            ### $port 
    63             my $sock = IO::Socket::SSL->new("$host:$port"); 
    64             croak IO::Socket::SSL::errstr() if ! $sock; 
    65             my $cert = $sock->peer_certificate(); 
    66  
    67             my $expire_date_asn1 = Net::SSLeay::X509_get_notAfter($cert); 
    68             my $expire_date_str  = Net::SSLeay::P_ASN1_UTCTIME_put2string($expire_date_asn1); 
    69             ### $expire_date_str 
    70             my $begin_date_asn1  = Net::SSLeay::X509_get_notBefore($cert); 
    71             my $begin_date_str   = Net::SSLeay::P_ASN1_UTCTIME_put2string($begin_date_asn1); 
    72             ### $begin_date_str 
    73  
    74             $sock->close; 
     85            my $cert = _peer_certificate($host, $port); 
     86            my $x509 = Crypt::OpenSSL::X509->new_from_string($cert, FORMAT_ASN1); 
     87            my $begin_date_str  = $x509->notBefore; 
     88            my $expire_date_str = $x509->notAfter; 
    7589 
    7690            $self->{expire_date} = DateTime->from_epoch(epoch => str2time($expire_date_str)); 
     
    120134} 
    121135 
     136sub _peer_certificate { 
     137    my($host, $port) = @_; 
     138 
     139    my $cert; 
     140 
     141    no warnings 'once'; 
     142    *IO::Socket::INET::write_atomically = sub { 
     143        my($self, $data) = @_; 
     144 
     145        my $length    = length $data; 
     146        my $offset    = 0; 
     147        my $read_byte = 0; 
     148 
     149        while ($length > 0) { 
     150            my $r = $self->syswrite($data, $length, $offset) || last; 
     151            $offset    += $r; 
     152            $length    -= $r; 
     153            $read_byte += $r; 
     154        } 
     155 
     156        return $read_byte; 
     157    }; 
     158 
     159    my $sock = IO::Socket::INET->new( 
     160        PeerAddr => $host, 
     161        PeerPort => $port, 
     162        Proto    => 'tcp', 
     163       ) or croak "cannot create socket: $!"; 
     164 
     165    _send_client_hello($sock); 
     166 
     167    my $do_loop = 1; 
     168    while ($do_loop) { 
     169        my $record = _get_record($sock) or croak $!; 
     170        croak "record type is not HANDSHAKE" if $record->{type} != $SSL3_RT_HANDSHAKE; 
     171 
     172        while (my $handshake = _get_handshake($record)) { 
     173            croak "too many loop" if $do_loop++ >= 10; 
     174            if ($handshake->{type} == $SSL3_MT_HELLO_REQUEST) { 
     175                ; 
     176            } elsif ($handshake->{type} == $SSL3_MT_CERTIFICATE_REQUEST) { 
     177                ; 
     178            } elsif ($handshake->{type} == $SSL3_MT_SERVER_HELLO) { 
     179                ; 
     180            } elsif ($handshake->{type} == $SSL3_MT_CERTIFICATE) { 
     181                my $data = $handshake->{data}; 
     182                my $len1 = $handshake->{length}; 
     183                my $len2 = (vec($data, 0, 8)<<16)+(vec($data, 1, 8)<<8)+vec($data, 2, 8); 
     184                my $len3 = (vec($data, 3, 8)<<16)+(vec($data, 4, 8)<<8)+vec($data, 5, 8); 
     185                croak "X509: length error" if $len1 != $len2 + 3; 
     186                $cert = substr $data, 6; # DER format 
     187            } elsif ($handshake->{type} == $SSL3_MT_SERVER_KEY_EXCHANGE) { 
     188                ; 
     189            } elsif ($handshake->{type} == $SSL3_MT_SERVER_DONE) { 
     190                $do_loop = 0; 
     191            } else { 
     192                ; 
     193            } 
     194        } 
     195 
     196    } 
     197 
     198    _sendalert($sock, $SSL3_AL_FATAL, $SSL3_AD_HANDSHAKE_FAILURE) or croak $!; 
     199    $sock->close; 
     200 
     201    return $cert; 
     202} 
     203 
     204sub _send_client_hello { 
     205    my($sock) = @_; 
     206 
     207    my(@buf,$len); 
     208    ## record 
     209    push @buf, $SSL3_RT_HANDSHAKE; 
     210    push @buf, 3, 0; 
     211    push @buf, undef, undef; 
     212    my $pos_record_len = $#buf-1; 
     213 
     214    ## handshake 
     215    push @buf, $SSL3_MT_CLIENT_HELLO; 
     216    push @buf, undef, undef, undef; 
     217    my $pos_handshake_len = $#buf-2; 
     218 
     219    ## ClientHello 
     220    # client_version 
     221    push @buf, 3, 0; 
     222    # random 
     223    my $time = time; 
     224    push @buf, (($time>>24) & 0xFF); 
     225    push @buf, (($time>>16) & 0xFF); 
     226    push @buf, (($time>> 8) & 0xFF); 
     227    push @buf, (($time    ) & 0xFF); 
     228    push @buf, ((0xFF) x 28); 
     229    # session_id 
     230    push @buf, 0; 
     231    # cipher_suites 
     232    $len = 27 * 2; 
     233    push @buf, (($len >> 8) & 0xFF); 
     234    push @buf, (($len     ) & 0xFF); 
     235    for (my $i=1; $i<=27; $i++) { 
     236        push @buf, (($i >> 8) & 0xFF); 
     237        push @buf, (($i     ) & 0xFF); 
     238    } 
     239    # compression 
     240    push @buf, 1; 
     241    push @buf, 0; 
     242 
     243    # record length 
     244    $len = scalar(@buf) - $pos_record_len - 2; 
     245    $buf[ $pos_record_len   ] = (($len >>  8) & 0xFF); 
     246    $buf[ $pos_record_len+1 ] = (($len      ) & 0xFF); 
     247 
     248    # handshake length 
     249    $len = scalar(@buf) - $pos_handshake_len - 3; 
     250    $buf[ $pos_handshake_len   ] = (($len >> 16) & 0xFF); 
     251    $buf[ $pos_handshake_len+1 ] = (($len >>  8) & 0xFF); 
     252    $buf[ $pos_handshake_len+2 ] = (($len      ) & 0xFF); 
     253 
     254    my $data; 
     255    $data .= pack('C', $_) for @buf; 
     256 
     257    return $sock->write_atomically($data); 
     258} 
     259 
     260sub _get_record { 
     261    my($sock) = @_; 
     262 
     263    my $record = { 
     264        type    => -1, 
     265        version => -1, 
     266        length  => -1, 
     267        read    =>  0, 
     268        data    => "", 
     269    }; 
     270 
     271    $sock->read($record->{type}   , 1) or croak $!; 
     272    $record->{type} = unpack 'C', $record->{type}; 
     273 
     274    $sock->read($record->{version}, 2) or croak $!; 
     275    $record->{version} = unpack 'n', $record->{version}; 
     276 
     277    $sock->read($record->{length},  2) or croak $!; 
     278    $record->{length}  = unpack 'n', $record->{length}; 
     279 
     280    $sock->read($record->{data},    $record->{length}) or croak $!; 
     281 
     282    return $record; 
     283} 
     284 
     285sub _get_handshake { 
     286    my($record) = @_; 
     287 
     288    my $handshake = { 
     289        type   => -1, 
     290        length => -1, 
     291        data   => "", 
     292       }; 
     293 
     294    return if $record->{read} >= $record->{length}; 
     295 
     296    $handshake->{type}   = vec($record->{data}, $record->{read}++, 8); 
     297    return if $record->{read} + 3 > $record->{length}; 
     298 
     299    $handshake->{length} = 
     300         (vec($record->{data}, $record->{read}++, 8)<<16) 
     301        +(vec($record->{data}, $record->{read}++, 8)<< 8) 
     302        +(vec($record->{data}, $record->{read}++, 8)    ); 
     303 
     304    if ($handshake->{length} > 0) { 
     305        $handshake->{data} = substr($record->{data}, $record->{read}, $handshake->{length}); 
     306        $record->{read} += $handshake->{length}; 
     307        return if $record->{read} > $record->{length}; 
     308    } else { 
     309        $handshake->{data}= undef; 
     310    } 
     311 
     312    return $handshake; 
     313} 
     314 
     315sub _sendalert { 
     316    my($sock, $level, $desc) = @_; 
     317 
     318    my $data = ""; 
     319 
     320    $data .= pack('C', $SSL3_RT_ALERT); 
     321    $data .= pack('C', 3); 
     322    $data .= pack('C', 0); 
     323    $data .= pack('C', 0); 
     324    $data .= pack('C', 2); 
     325    $data .= pack('C', $level); 
     326    $data .= pack('C', $desc); 
     327 
     328    return $sock->write_atomically($data); 
     329} 
    122330 
    1233311; # Magic true value required at end of module