#!/usr/bin/perl

# myproxy admin adduser/addservice script
# generates a certificate request using grid-cert-request
# signs the request using grid-ca-sign
# and stores the credential in the repository using
#   myproxy-admin-load-credential

use File::Temp qw(tempdir);
use File::Basename;
use IPC::Open3;

# check for program dependencies

chomp($grid_cert_request = `which grid-cert-request 2>/dev/null`);
if (!(-x $grid_cert_request)) {
    print STDERR "Error: grid-cert-request not found.\n";
    exit 1;
}
chomp($grid_ca_sign = `which grid-ca-sign 2>/dev/null`);
if (!(-x $grid_ca_sign)) {
    print STDERR "Error: grid-ca-sign not found.\n";
    print STDERR "Is the Simple CA package installed?\n";
    exit 1;
}
chomp($mpalc = `which myproxy-admin-load-credential 2>/dev/null`);
if (!(-x $mpalc)) {
    print STDERR "Error: myproxy-admin-load-credential not found.\n";
    print STDERR "Is MyProxy installed in $ENV{GLOBUS_LOCATION}?\n";
    exit 1;
}
chomp($grid_cert_info = `which grid-cert-info 2>/dev/null`);
if (!(-x $grid_cert_info)) {
    print STDERR "Error: grid-cert-info not found.\n";
    exit 1;
}

$cmdname = basename($0);

# handle arguments

require "getopts.pl";

if (!&Getopts('huvc:p:s:l:t:aAxXr:R:ndk:K:Z:E:') || $opt_h || $opt_u) {
    print STDERR <<"EOF";
Syntax: $0 [-c cn] [-l username] ...
        $0 [-usage|-help]
   Options
       -h                Displays usage
       -u
       -v                Display debugging messages
       -c <cn>           Common Name for new credential
       -s <directory>    Credential storage directory
       -l <username>     Credential username
       -t <hours>        Max. lifetime of delegated proxies
       -p <CA password>  Set CA private key password using
                         openssl format (see the PASS PHRASE ARGUMENTS
                         section in the openssl(1) man page)
       -a                Allow credentials to be retrieved with
                         just username/passphrase
       -A                Allow credentials to be renewed by
                         any client (not recommended)
       -x                Set regular expression matching mode
                         for following policy options
       -X                Set CN matching mode (default)
                         for following policy options
       -r <dn>           Allow specified entity to retrieve credential
       -R <dn>           Allow specified entity to renew credential
       -Z <dn>           Allow specified entity to retrieve credential
                         w/o passphrase
       -E <dn>           Allow specified entity to retrieve credential key
       -n                Disable passphrase authentication
       -d                Use the proxy certificate subject as username
       -k <name>         Specifies credential name
       -K <desc>         Specifies credential description
EOF
    exit(1);
}

#grid-cert-request

if (!$opt_c) {
    print "Enter common name for the certificate: ";
    chop($opt_c = <STDIN>);
}

$tmp_dir_name = tempdir("myproxy_adduser_XXXXXX", TMPDIR => 1, CLEANUP => 1);
$prefix="myproxy_adduser_";
$certfile = "${tmp_dir_name}/${prefix}cert.pem";
$reqfile = "${tmp_dir_name}/${prefix}cert_request.pem";
$keyfile = "${tmp_dir_name}/${prefix}key.pem";

if ($opt_v) {
  print "temporary directory is: $tmp_dir_name\n";
}

@args = ("grid-cert-request", "-cn", $opt_c, "-prefix", $prefix, "-dir",
         $tmp_dir_name,  "-force");
push(@args, "-nopassphrase") if ($opt_n);
&runcmd(@args);

#grid-ca-sign

@args = ("grid-ca-sign", "-in", $reqfile, "-out", $certfile, "-force");
if ($opt_p) {
  push(@args, "-passin"); push(@args, $opt_p);
}

if ($opt_v) {
  print "running command:\n@args\n";
}

if (system(@args)) {
    print STDERR "grid-ca-sign failed.\n";
    &cleanup();
    exit 1;
}

#myproxy-alcf

if (!$opt_l && !$opt_d) {
    print "Enter username [use DN by default]: ";
    chop ($opt_l = <STDIN>);
    if (length $opt_l == 0) {
	$opt_d = 1;
    }
}

@args = ("myproxy-admin-load-credential", "-c", $certfile, "-y", $keyfile);
if ($opt_s) {
    push(@args, "-s"); push(@args, $opt_s);
}
if ($opt_l) {
    push(@args, "-l"); push(@args, $opt_l);
}
if ($opt_t) {
    push(@args, "-t"); push(@args, $opt_t);
}
push(@args, "-a") if ($opt_a);
push(@args, "-A") if ($opt_A);
push(@args, "-x") if ($opt_x);
push(@args, "-X") if ($opt_X);
if ($opt_r) {
    push(@args, "-r"); push(@args, $opt_r);
}
if ($opt_R) {
    push(@args, "-R"); push(@args, $opt_R);
}
if ($opt_Z) {
    push(@args, "-Z"); push(@args, $opt_Z);
}
if ($opt_E) {
    push(@args, "-E"); push(@args, $opt_E);
} elsif ($cmdname eq "myproxy-admin-addservice") {
    push(@args, "-x"); push(@args, "-E"); push(@args, "\*");
}
push(@args, "-d") if ($opt_d);
if ($opt_k) {
    push(@args, "-k"); push(@args, $opt_k);
} elsif ($opt_c && $cmdname eq "myproxy-admin-addservice") {
    push(@args, "-k"); push(@args, $opt_c);
}
if ($opt_K) {
    push(@args, "-K"); push(@args, $opt_K);
}

if ($opt_v) {
  print "running command:\n@args\n";
}

if (system(@args)) {
    print STDERR "myproxy-admin-load-credential failed.\n";
    &cleanup();
    exit 1;
}

print "Certificate subject is:\n";
@args = ("grid-cert-info", "-subject", "-file", $certfile);
system(@args);

&cleanup();
exit 0;

sub cleanup {
    unlink($certfile) if (defined($certfile));
    unlink($reqfile) if (defined($reqfile));
    if (defined($keyfile)) {
	&wipefile($keyfile);
	unlink($keyfile);
    }
    # temporary directory is automatically removed by File::Temp
}

sub wipefile {
    local($filename) = @_;
    $size = (stat($filename))[7];
    return if (!defined($size) || !$size);
    return if (!open(WIPEFILE, '>', $filename));
    for ($i = 0; $i < $size; $i++) {
	print WIPEFILE "\0";
    }
    close(WIPEFILE);
}

sub runcmd {
  @args = @_;
  if ($opt_v) {
    print "running command:\n@args\n";
  }
  $pid = open3(*Writer, *Reader, '', @args);
  close(Writer);
  @output = <Reader>;
  $output = join('', @output);
  close(Reader);
  waitpid($pid, 0);
  if ($?) {
    print STDERR $args[0], " failed:\n";
    print STDERR $output;
    exit 1;
  } elsif ($opt_v) {
    print "command output:\n$output\n";
  }
}
