Home | History | Annotate | Line # | Download | only in apps
      1  1.1  christos #!{- $config{HASHBANGPERL} -}
      2  1.1  christos # Copyright 2002-2018 The OpenSSL Project Authors. All Rights Reserved.
      3  1.1  christos # Copyright (c) 2002 The OpenTSA Project. All rights reserved.
      4  1.1  christos #
      5  1.1  christos # Licensed under the Apache License 2.0 (the "License").  You may not use
      6  1.1  christos # this file except in compliance with the License.  You can obtain a copy
      7  1.1  christos # in the file LICENSE in the source distribution or at
      8  1.1  christos # https://www.openssl.org/source/license.html
      9  1.1  christos 
     10  1.1  christos use strict;
     11  1.1  christos use IO::Handle;
     12  1.1  christos use Getopt::Std;
     13  1.1  christos use File::Basename;
     14  1.1  christos use WWW::Curl::Easy;
     15  1.1  christos 
     16  1.1  christos use vars qw(%options);
     17  1.1  christos 
     18  1.1  christos # Callback for reading the body.
     19  1.1  christos sub read_body {
     20  1.1  christos     my ($maxlength, $state) = @_;
     21  1.1  christos     my $return_data = "";
     22  1.1  christos     my $data_len = length ${$state->{data}};
     23  1.1  christos     if ($state->{bytes} < $data_len) {
     24  1.1  christos         $data_len = $data_len - $state->{bytes};
     25  1.1  christos         $data_len = $maxlength if $data_len > $maxlength;
     26  1.1  christos         $return_data = substr ${$state->{data}}, $state->{bytes}, $data_len;
     27  1.1  christos         $state->{bytes} += $data_len;
     28  1.1  christos     }
     29  1.1  christos     return $return_data;
     30  1.1  christos }
     31  1.1  christos 
     32  1.1  christos # Callback for writing the body into a variable.
     33  1.1  christos sub write_body {
     34  1.1  christos     my ($data, $pointer) = @_;
     35  1.1  christos     ${$pointer} .= $data;
     36  1.1  christos     return length($data);
     37  1.1  christos }
     38  1.1  christos 
     39  1.1  christos # Initialise a new Curl object.
     40  1.1  christos sub create_curl {
     41  1.1  christos     my $url = shift;
     42  1.1  christos 
     43  1.1  christos     # Create Curl object.
     44  1.1  christos     my $curl = WWW::Curl::Easy::new();
     45  1.1  christos 
     46  1.1  christos     # Error-handling related options.
     47  1.1  christos     $curl->setopt(CURLOPT_VERBOSE, 1) if $options{d};
     48  1.1  christos     $curl->setopt(CURLOPT_FAILONERROR, 1);
     49  1.1  christos     $curl->setopt(CURLOPT_USERAGENT,
     50  1.1  christos         "OpenTSA tsget.pl/openssl-{- $config{full_version} -}");
     51  1.1  christos 
     52  1.1  christos     # Options for POST method.
     53  1.1  christos     $curl->setopt(CURLOPT_UPLOAD, 1);
     54  1.1  christos     $curl->setopt(CURLOPT_CUSTOMREQUEST, "POST");
     55  1.1  christos     $curl->setopt(CURLOPT_HTTPHEADER,
     56  1.1  christos         ["Content-Type: application/timestamp-query",
     57  1.1  christos         "Accept: application/timestamp-reply,application/timestamp-response"]);
     58  1.1  christos     $curl->setopt(CURLOPT_READFUNCTION, \&read_body);
     59  1.1  christos     $curl->setopt(CURLOPT_HEADERFUNCTION, sub { return length($_[0]); });
     60  1.1  christos 
     61  1.1  christos     # Options for getting the result.
     62  1.1  christos     $curl->setopt(CURLOPT_WRITEFUNCTION, \&write_body);
     63  1.1  christos 
     64  1.1  christos     # SSL related options.
     65  1.1  christos     $curl->setopt(CURLOPT_SSLKEYTYPE, "PEM");
     66  1.1  christos     $curl->setopt(CURLOPT_SSL_VERIFYPEER, 1);    # Verify server's certificate.
     67  1.1  christos     $curl->setopt(CURLOPT_SSL_VERIFYHOST, 2);    # Check server's CN.
     68  1.1  christos     $curl->setopt(CURLOPT_SSLKEY, $options{k}) if defined($options{k});
     69  1.1  christos     $curl->setopt(CURLOPT_SSLKEYPASSWD, $options{p}) if defined($options{p});
     70  1.1  christos     $curl->setopt(CURLOPT_SSLCERT, $options{c}) if defined($options{c});
     71  1.1  christos     $curl->setopt(CURLOPT_CAINFO, $options{C}) if defined($options{C});
     72  1.1  christos     $curl->setopt(CURLOPT_CAPATH, $options{P}) if defined($options{P});
     73  1.1  christos     $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r});
     74  1.1  christos     $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g});
     75  1.1  christos 
     76  1.1  christos     # Setting destination.
     77  1.1  christos     $curl->setopt(CURLOPT_URL, $url);
     78  1.1  christos 
     79  1.1  christos     return $curl;
     80  1.1  christos }
     81  1.1  christos 
     82  1.1  christos # Send a request and returns the body back.
     83  1.1  christos sub get_timestamp {
     84  1.1  christos     my $curl = shift;
     85  1.1  christos     my $body = shift;
     86  1.1  christos     my $ts_body;
     87  1.1  christos     local $::error_buf;
     88  1.1  christos 
     89  1.1  christos     # Error-handling related options.
     90  1.1  christos     $curl->setopt(CURLOPT_ERRORBUFFER, "::error_buf");
     91  1.1  christos 
     92  1.1  christos     # Options for POST method.
     93  1.1  christos     $curl->setopt(CURLOPT_INFILE, {data => $body, bytes => 0});
     94  1.1  christos     $curl->setopt(CURLOPT_INFILESIZE, length(${$body}));
     95  1.1  christos 
     96  1.1  christos     # Options for getting the result.
     97  1.1  christos     $curl->setopt(CURLOPT_FILE, \$ts_body);
     98  1.1  christos 
     99  1.1  christos     # Send the request...
    100  1.1  christos     my $error_code = $curl->perform();
    101  1.1  christos     my $error_string;
    102  1.1  christos     if ($error_code != 0) {
    103  1.1  christos         my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE);
    104  1.1  christos         $error_string = "could not get timestamp";
    105  1.1  christos         $error_string .= ", http code: $http_code" unless $http_code == 0;
    106  1.1  christos         $error_string .= ", curl code: $error_code";
    107  1.1  christos         $error_string .= " ($::error_buf)" if defined($::error_buf);
    108  1.1  christos     } else {
    109  1.1  christos         my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE);
    110  1.1  christos         if (lc($ct) ne "application/timestamp-reply"
    111  1.1  christos             && lc($ct) ne "application/timestamp-response") {
    112  1.1  christos             $error_string = "unexpected content type returned: $ct";
    113  1.1  christos         }
    114  1.1  christos     }
    115  1.1  christos     return ($ts_body, $error_string);
    116  1.1  christos 
    117  1.1  christos }
    118  1.1  christos 
    119  1.1  christos # Print usage information and exists.
    120  1.1  christos sub usage {
    121  1.1  christos 
    122  1.1  christos     print STDERR "usage: $0 -h <server_url> [-e <extension>] [-o <output>] ";
    123  1.1  christos     print STDERR "[-v] [-d] [-k <private_key.pem>] [-p <key_password>] ";
    124  1.1  christos     print STDERR "[-c <client_cert.pem>] [-C <CA_certs.pem>] [-P <CA_path>] ";
    125  1.1  christos     print STDERR "[-r <file:file...>] [-g <EGD_socket>] [<request>]...\n";
    126  1.1  christos     exit 1;
    127  1.1  christos }
    128  1.1  christos 
    129  1.1  christos # ----------------------------------------------------------------------
    130  1.1  christos #   Main program
    131  1.1  christos # ----------------------------------------------------------------------
    132  1.1  christos 
    133  1.1  christos # Getting command-line options (default comes from TSGET environment variable).
    134  1.1  christos my $getopt_arg =  "h:e:o:vdk:p:c:C:P:r:g:";
    135  1.1  christos if (exists $ENV{TSGET}) {
    136  1.1  christos     my @old_argv = @ARGV;
    137  1.1  christos     @ARGV = split /\s+/, $ENV{TSGET};
    138  1.1  christos     getopts($getopt_arg, \%options) or usage;
    139  1.1  christos     @ARGV = @old_argv;
    140  1.1  christos }
    141  1.1  christos getopts($getopt_arg, \%options) or usage;
    142  1.1  christos 
    143  1.1  christos # Checking argument consistency.
    144  1.1  christos if (!exists($options{h}) || (@ARGV == 0 && !exists($options{o}))
    145  1.1  christos     || (@ARGV > 1 && exists($options{o}))) {
    146  1.1  christos     print STDERR "Inconsistent command line options.\n";
    147  1.1  christos     usage;
    148  1.1  christos }
    149  1.1  christos # Setting defaults.
    150  1.1  christos @ARGV = ("-") unless @ARGV != 0;
    151  1.1  christos $options{e} = ".tsr" unless defined($options{e});
    152  1.1  christos 
    153  1.1  christos # Processing requests.
    154  1.1  christos my $curl = create_curl $options{h};
    155  1.1  christos undef $/;   # For reading whole files.
    156  1.1  christos REQUEST: foreach (@ARGV) {
    157  1.1  christos     my $input = $_;
    158  1.1  christos     my ($base, $path) = fileparse($input, '\.[^.]*');
    159  1.1  christos     my $output_base = $base . $options{e};
    160  1.1  christos     my $output = defined($options{o}) ? $options{o} : $path . $output_base;
    161  1.1  christos 
    162  1.1  christos     STDERR->printflush("$input: ") if $options{v};
    163  1.1  christos     # Read request.
    164  1.1  christos     my $body;
    165  1.1  christos     if ($input eq "-") {
    166  1.1  christos         # Read the request from STDIN;
    167  1.1  christos         $body = <STDIN>;
    168  1.1  christos     } else {
    169  1.1  christos         # Read the request from file.
    170  1.1  christos         open INPUT, "<" . $input
    171  1.1  christos             or warn("$input: could not open input file: $!\n"), next REQUEST;
    172  1.1  christos         $body = <INPUT>;
    173  1.1  christos         close INPUT
    174  1.1  christos             or warn("$input: could not close input file: $!\n"), next REQUEST;
    175  1.1  christos     }
    176  1.1  christos 
    177  1.1  christos     # Send request.
    178  1.1  christos     STDERR->printflush("sending request") if $options{v};
    179  1.1  christos 
    180  1.1  christos     my ($ts_body, $error) = get_timestamp $curl, \$body;
    181  1.1  christos     if (defined($error)) {
    182  1.1  christos         die "$input: fatal error: $error\n";
    183  1.1  christos     }
    184  1.1  christos     STDERR->printflush(", reply received") if $options{v};
    185  1.1  christos 
    186  1.1  christos     # Write response.
    187  1.1  christos     if ($output eq "-") {
    188  1.1  christos         # Write to STDOUT.
    189  1.1  christos         print $ts_body;
    190  1.1  christos     } else {
    191  1.1  christos         # Write to file.
    192  1.1  christos         open OUTPUT, ">", $output
    193  1.1  christos             or warn("$output: could not open output file: $!\n"), next REQUEST;
    194  1.1  christos         print OUTPUT $ts_body;
    195  1.1  christos         close OUTPUT
    196  1.1  christos             or warn("$output: could not close output file: $!\n"), next REQUEST;
    197  1.1  christos     }
    198  1.1  christos     STDERR->printflush(", $output written.\n") if $options{v};
    199  1.1  christos }
    200  1.1  christos $curl->cleanup();
    201