| 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 | | } |
| | 19 | my $SSL3_RT_CHANGE_CIPHER_SPEC = 20; |
| | 20 | my $SSL3_RT_ALERT = 21; |
| | 21 | my $SSL3_RT_HANDSHAKE = 22; |
| | 22 | my $SSL3_RT_APPLICATION_DATA = 23; |
| | 23 | |
| | 24 | my $SSL3_MT_HELLO_REQUEST = 0; |
| | 25 | my $SSL3_MT_CLIENT_HELLO = 1; |
| | 26 | my $SSL3_MT_SERVER_HELLO = 2; |
| | 27 | my $SSL3_MT_CERTIFICATE = 11; |
| | 28 | my $SSL3_MT_SERVER_KEY_EXCHANGE = 12; |
| | 29 | my $SSL3_MT_CERTIFICATE_REQUEST = 13; |
| | 30 | my $SSL3_MT_SERVER_DONE = 14; |
| | 31 | my $SSL3_MT_CERTIFICATE_VERIFY = 15; |
| | 32 | my $SSL3_MT_CLIENT_KEY_EXCHANGE = 16; |
| | 33 | my $SSL3_MT_FINISHED = 20; |
| | 34 | |
| | 35 | my $SSL3_AL_WARNING = 0x01; |
| | 36 | my $SSL3_AL_FATAL = 0x02; |
| | 37 | |
| | 38 | my $SSL3_AD_CLOSE_NOTIFY = 0; |
| | 39 | my $SSL3_AD_UNEXPECTED_MESSAGE = 10; # fatal |
| | 40 | my $SSL3_AD_BAD_RECORD_MAC = 20; # fatal |
| | 41 | my $SSL3_AD_DECOMPRESSION_FAILURE = 30; # fatal |
| | 42 | my $SSL3_AD_HANDSHAKE_FAILURE = 40; # fatal |
| | 43 | my $SSL3_AD_NO_CERTIFICATE = 41; |
| | 44 | my $SSL3_AD_BAD_CERTIFICATE = 42; |
| | 45 | my $SSL3_AD_UNSUPPORTED_CERTIFICATE = 43; |
| | 46 | my $SSL3_AD_CERTIFICATE_REVOKED = 44; |
| | 47 | my $SSL3_AD_CERTIFICATE_EXPIRED = 45; |
| | 48 | my $SSL3_AD_CERTIFICATE_UNKNOWN = 46; |
| | 49 | my $SSL3_AD_ILLEGAL_PARAMETER = 47; # fatal |
| | 136 | sub _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 | |
| | 204 | sub _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 | |
| | 260 | sub _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 | |
| | 285 | sub _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 | |
| | 315 | sub _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 | } |