bin/ca
changeset 2 72112c207284
parent 1 f44419b55cf0
equal deleted inserted replaced
1:f44419b55cf0 2:72112c207284
     6 use File::Path;
     6 use File::Path;
     7 use File::Temp qw(tempdir);
     7 use File::Temp qw(tempdir);
     8 use File::Basename;
     8 use File::Basename;
     9 use Getopt::Long qw(GetOptionsFromArray);
     9 use Getopt::Long qw(GetOptionsFromArray);
    10 use Pod::Usage;
    10 use Pod::Usage;
       
    11 use feature qw(switch);
    11 
    12 
    12 my $CA_CRT = "CA/ca-crt.pem";
    13 my $CA_CRT = "CA/ca-crt.pem";
    13 my $CA_KEY = "CA/private/ca-key.pem";
    14 my $CA_KEY = "CA/private/ca-key.pem";
    14 my $CA_DIR = "./var";
    15 my $CA_DIR = "./var";
    15 
    16 
    16 my %TEMPLATE = (
    17 my %TEMPLATE = (
    17     ca  => "templates/ca",
    18     ca  => "lib/templates/ca",
    18     req => "templates/req",
    19     req => "lib/templates/req",
    19 );
    20 );
    20 
    21 
    21 my $TMP = tempdir("/tmp/$ENV{USER}.ca.XXXXXX", CLEANUP => 1);
    22 my $TMP = tempdir("/tmp/$ENV{USER}.ca.XXXXXX", CLEANUP => 1);
    22 
    23 
    23 my $opt_days    = undef;    # see the templates/ca for a default
    24 my $opt_days    = undef;    # see the templates/ca for a default
    35     GetOptions(
    36     GetOptions(
    36         "d|days=i"    => \$opt_days,
    37         "d|days=i"    => \$opt_days,
    37         "t|type=s"    => \$opt_type,
    38         "t|type=s"    => \$opt_type,
    38         "p|policy=s"  => \$opt_policy,
    39         "p|policy=s"  => \$opt_policy,
    39         "o|outfile=s" => \$opt_outfile,
    40         "o|outfile=s" => \$opt_outfile,
    40         "force"       => \$opt_force,
    41         "f|force"     => \$opt_force,
    41         "init"        => sub { init_ca(); exit 0; },
    42         "i|init"      => sub { eval { init_ca() }; if ($@) { warn $@; exit 1 }; exit 0 },
    42         "h|help"      => sub { pod2usage(-verbose => 1, -exit => 0) },
    43         "h|help"      => sub { pod2usage(-verbose => 1, -exit => 0) },
    43         "m|man"       => sub { pod2usage(-verbose => 2, -exit => 0) },
    44         "m|man"       => sub { pod2usage(-verbose => 2, -exit => 0) },
    44     ) or pod2usage;
    45     ) or pod2usage;
    45 
    46 
    46     pod2usage if @ARGV > 1;
    47     pod2usage if @ARGV > 1;
    47     $csrfile = $ARGV[0];    # don't shift, we'll need it later!
    48     $csrfile = $ARGV[0];    # don't shift, we'll need it later!
    48 
    49 
    49     my $csr = new IO::File "$TMP/csr" => "w+"
    50     my $cnf = new IO::File ">$TMP/cnf"  or die "Can't open >$TMP/cnf: $!\n";
    50       or die "Can't open +>$TMP/csr: $!\n";
    51     my $csr = new IO::File "+>$TMP/csr" or die "Can't open +>$TMP/csr: $!\n";
    51     my $cnf = new IO::File "$TMP/cnf" => "w"
    52     my $crt = new IO::File "+>$TMP/crt" or die "Can't open +>$TMP/crt: $!\n";
    52       or die "Can't open >$TMP/cnf: $!\n";
    53     my $tt2 = new Template              or die $Template::ERROR;
    53     my $crt = new IO::File "$TMP/crt" => "w+"
       
    54       or die "Can't open +>$TMP/crt: $!\n";
       
    55     my $tt2 = new Template or die $Template::ERROR;
       
    56 
    54 
    57     # get a private copy of the request
    55     # get a private copy of the request
    58     print { IO::File->new("|openssl req -out $TMP/csr") } <>;
    56     print { IO::File->new("|openssl req -out $TMP/csr") } <>;
    59     open(STDIN, "</dev/tty") if not defined $csrfile;
    57     open(STDIN, "</dev/tty") if not defined $csrfile;
    60 
    58 
    84         elsif (/(.*[\W_])req$/)             { $outfile = "$1crt" }
    82         elsif (/(.*[\W_])req$/)             { $outfile = "$1crt" }
    85         else                                { $outfile .= ".crt.pem" }
    83         else                                { $outfile .= ".crt.pem" }
    86     }
    84     }
    87 
    85 
    88     # to be sure not to have an invalid/dangerous file name
    86     # to be sure not to have an invalid/dangerous file name
    89     fork() or do {
    87     if (fork() == 0) {
    90         open(STDOUT, ">$outfile")
    88 	if (defined $outfile) {
    91           if defined $outfile
    89 	    open(STDOUT, ">$outfile")
    92               or die "Can't open >$outfile: $!\n";
    90               or die "Can't open >$outfile: $!\n";
       
    91 	    }
    93         exec "openssl x509 -in $TMP/crt";
    92         exec "openssl x509 -in $TMP/crt";
    94         die "Can't exec openssl x509: $!\n";
    93         die "Can't exec openssl x509: $!\n";
    95     };
    94     }
    96     wait;
    95     else { wait }
       
    96 
       
    97     # and now, since it's finally done, we'll copy the request
       
    98     # away (for later use (thing about re-issuing a certificate))
       
    99     my $subject = `openssl x509 -in $TMP/crt -noout -subject`;
       
   100     if (my ($cn) = $subject =~ /CN=(\S+?)[,\/\s\$]/) {
       
   101         if (fork() == 0) {
       
   102             open(STDOUT, ">$CA_DIR/requests/$cn-csr.pem")
       
   103               or die "Can't open >$CA_DIR/requests/$cn-csr.pem: $!\n";
       
   104             exec "openssl req -in $TMP/csr";
       
   105             die "Can't exec openssl req: $!\n";
       
   106         }
       
   107         else { wait }
       
   108     }
       
   109     else {
       
   110         die "Can't determine the CN from $subject, not saving the request\n";
       
   111     }
       
   112 
    97     exit;
   113     exit;
    98 }
       
    99 
       
   100 sub verbose($) {
       
   101     warn $_[0], " \n ";
       
   102 }
   114 }
   103 
   115 
   104 sub ask_pass($) {
   116 sub ask_pass($) {
   105     my $prompt = shift;
   117     my $prompt = shift;
   106     my @keys = ("x", "y");
   118     my @keys = ("x", "y");
   125 sub init_ca() {
   137 sub init_ca() {
   126 
   138 
   127     # initialize the CA directory structure. This should
   139     # initialize the CA directory structure. This should
   128     # correspond to the values found in templates/ca
   140     # correspond to the values found in templates/ca
   129     die "$CA_DIR already exists" if -d $CA_DIR and not $opt_force;
   141     die "$CA_DIR already exists" if -d $CA_DIR and not $opt_force;
   130     mkpath(map { "$CA_DIR/$_" } qw(newcerts));
   142     mkpath(map { "$CA_DIR/$_" } qw(newcerts requests));
   131     mkpath(map { dirname $_ } $CA_CRT, $CA_KEY);
   143     mkpath(map { dirname $_ } $CA_CRT, $CA_KEY);
   132     (new IO::File ">$CA_DIR/index");
   144     (new IO::File ">$CA_DIR/index");
   133     (new IO::File ">$CA_DIR/serial")->print("01\n");
   145     (new IO::File ">$CA_DIR/serial")->print("01\n");
   134 
   146 
   135     # now
   147     # now
   152     system(
   164     system(
   153 "openssl rsa -in $TMP/ca-key.pem -des3 -passin env:CA_PASS -passout env:CA_PASS -out $CA_KEY"
   165 "openssl rsa -in $TMP/ca-key.pem -des3 -passin env:CA_PASS -passout env:CA_PASS -out $CA_KEY"
   154     ) and exit;
   166     ) and exit;
   155     umask($_);
   167     umask($_);
   156 
   168 
       
   169     return 0;
       
   170 
   157 }
   171 }
   158 
   172 
   159 __END__
   173 __END__
   160 
   174 
   161 =head1 NAME
   175 =head1 NAME