## OpenCA - CA Command
## (c) 1998-2001 by Massimiliano Pala and OpenCA Group
##
##   File Name: bpApprovePendingCSR
##     Version: $Revision: 1.7.2.1 $
##       Brief: approve authorized pending CSRs
## Description: approve pending CSRs signed by operator and aksing for role
##  Parameters: key, dataType, passwd

sub cmdBpApprovePendingCSR {

## get the parameters
## Get the parameters
my $operator = $query->param('operator');
my $role     = $query->param('role');
my $passwd   = $query->param('passwd');

my ($request, $operator_cert, $operator_serial, $signature, $role_sig, $cert);

if (not $operator or not $role or not $passwd) {
	## must be the startpage

	## load page
	my $page = $tools->getFile( getRequired ('BP_ApprovePendingCSRSheet'));
	configError ( gettext ("Cannot load the form for the batch processor for approving pending certificate requests"))
		if (not $page);

        ## set values
	my $html = $query->newInput (
				-regx   => 'LETTERS',
				-intype => 'textfield',
				-size   => 30,
				-name   => 'operator',
				-check  => 'fill',
				-minlen => 3,
				-value  => $operator);
	$page = $query->subVar($page,'@OPERATOR@', $html);
	$html = $query->newInput (
				-regx=>'LETTERS',
				-intype=>'popup_menu',
				-name=>'role',
				-values=>[loadRoles()],
				-default=>$role);
	$page = $query->subVar($page,'@ROLE@', $html);

	## display
	print $page;

	return 1;
}

print startLogPage (gettext ("Approve Pending Certificate Requests Batch Processor"));

print addLogSection (gettext ("Operator's serial ... "));
print addLogLine    ($operator);
print closeLogSection ();

print addLogSection (gettext ("Requested role ... "));
print addLogLine    ($role);
print closeLogSection ();

print addLogSection (gettext ("Load Operator's certificate and key ..."));
my $operator_cert = $db->getItem (DATATYPE => "CERTIFICATE", KEY => $operator);
if (not $operator_cert) {
	print addPreLogLine (gettext ("Cannot load operator's certificate from database!"));
	print closeLogSection ();
	print closeLogPage();
	return undef;
}
if (not $operator_cert->getParsed()->{KEY}) {
	print addPreLogLine ( gettext ("Operator's certificate doesn't contain a private key!"));
	print closeLogSection ();
	print closeLogPage();
	return undef;
}
print addLogLine (gettext ("ok."));
print closeLogSection ();

my $batch_dir = getRequired ("BP_DIR");
my $tmpdir    = getRequired ("TempDir");

print addLogSection ( gettext ("Running batch processor ..."));

## loop
## there can never be a request 0
my $key = undef;
while ($request = $db->getNextItem(DATATYPE => "PENDING_REQUEST", KEY => $key) and
       $request->getSerial() != $key) {

	$key = $request->getSerial ();

	## get the ID
	my $ID = $request->getParsed()->{DN_HASH}->{CN}[0];

	## build the directory path for the ID
	my $id_dir = $batch_dir;
	my $h_id   = $ID;
	while ($h_id !~ /^$/) {
		$id_dir .= "/".substr $h_id, 0, 1;
		$h_id =~ s/^.//;
	}

	## check for automatic handling
	if (not -d $id_dir) {
		# this is not an error !
		next;
	}

	## check the role
	if ($role ne $tools->getFile ($id_dir."/data/ROLE")) {
		# this is not an error !
		next;
	}

	## check for the ID
	if (not -f $id_dir."/ID") {
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the ID is not present in the directory __DIR__",
                                "__CSR_SERIAL__", $request->getSerial(),
				                "__DIR__", $id_dir));
		next;
	}
	if ($tools->getFile ($id_dir."/ID") ne $ID) {
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the ID is __ID__ in the request and __FILE_ID__ in the file __FILE__",
                                "__CSR_SERIAL__", $request->getSerial(),
				                "__ID__", $ID,
				                "__FILE_ID__", $tools->getFile ($id_dir."/ID"),
                                "__FILE__", $id_dir."/ID"));
		next;
	}

	## check for the DN
	my $DN = $tools->getFile ($id_dir."/data/DN");
	if (not $DN) {
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the DN is not present in the directory __DIR__.",
                                "__CSR_SERIAL__", $request->getSerial(),
				                "__DIR__", $id_dir));
		next;
	}

	## check for the SUBJECT_ALT_NAME
	my $SUBJECT_ALT_NAME = $tools->getFile ($id_dir."/data/SUBJECT_ALT_NAME");
	if (not $SUBJECT_ALT_NAME) {
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the SUBJECT_ALT_NAME is not present in the directory __DIR__.",
                                "__CSR_SERIAL__", $request->getSerial(),
				                "__DIR__", $id_dir));
		next;
	}

	## check for the permission
	if (not -f $id_dir."/acl/approvePendingCSR") {
		# this is not an error !
		next;
	}

	## compare the hashed PINs
	if (not -f $id_dir."/public/hashedPIN") {
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the hashed PIN is not present in the directory __DIR__.",
                                "__CSR_SERIAL__", $request->getSerial(),
                                "__DIR__", $id_dir));
		next;
	}
	if ($tools->getFile ($id_dir."/public/hashedPIN") ne
	    $request->getParsed()->{HEADER}->{PIN}) {
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the hashed PIN is __PIN__ in the request and __FILE_PIN__ in the file __FILE__.",
                                "__CSR_SERIAL__", $request->getSerial(),
				                "__PIN__", $request->getParsed()->{HEADER}->{PIN},
				                "__FILE_PIN__", $tools->getFile ($id_dir."/public/hashedPIN"),
                                "__FILE__", $id_dir."/public/hashedPIN"));
		next;
	}

	## check the public key
	## Check if there are certificates with the same keys
	my @certList = $db->searchItems(DATATYPE => "CERTIFICATE",
					PUBKEY   => $request->getParsed()->{PUBKEY});
	if($#certList > -1) {
		my $errorString = gettext ("A Certificate with the same public key exists!")."<br>\n".
				gettext ("This is a keycompromise of the certificates with the serial:")."\n".
				"<ul>\n";
		foreach my $h (@certList) {
			$errorString .= "<li>".$h->getSerial()."</li>\n";
		}
		$errorString .= gettext ("Please revoke the certificates and delete the request.")."\n";
		print addPreLogLine ( i18nGettext (
                                "CSR __CSR_SERIAL__ ignored because the used public key is already in use.",
                                "__CSR_SERIAL__", $request->getSerial()));
		print addPreLogLine ("<FONT COLOR=#ff0000>".$errorString."</FONT>");
		next;
	}

	## build request
	my ($header, $text);

	my $beginHeader = "-----BEGIN HEADER-----";
	my $endHeader   = "-----END HEADER-----";
	my $parsed_req  = $request->getParsed();

	$header  = "$beginHeader\n";
	$header .= "TYPE = $parsed_req->{TYPE}\n";
	$header .= "RA = $parsed_req->{HEADER}->{RA}\n";
	$header .= "SERIAL = ".$request->getSerial()."\n";
	$header .= "RENEW = ".$parsed_req->{HEADER}->{RENEW}."\n" if ($parsed_req->{HEADER}->{RENEW});
	$header .= "OPERATOR = ".$operator."\n"
		if ($operator);
	$header .= "NOTBEFORE = $parsed_req->{HEADER}->{NOTBEFORE}\n";
	$header .= "APPROVED = ".$tools->getDate()."\n";
	$header .= "PIN = $parsed_req->{HEADER}->{PIN}\n";
	$header .= "SUBJECT = $DN\n";
	$header .= "ROLE = $role\n";
	$header .= "SUBJECT_ALT_NAME = $SUBJECT_ALT_NAME\n";
	$header .= "$endHeader\n";

	$text = $parsed_req->{BODY};

	my $text_req = $header.$text;

	## sign
	if (not $cryptoShell->sign (
				DATA     => $text_req,
				KEY      => $operator_cert->getParsed()->{KEY},
				CERT     => $operator_cert->getPEM(),
				OUT_FILE => $tmpdir."/${$}_request.sig",
				PASSWD   => $passwd)) {
		print addPreLogLine ("<FONT COLOR=#FF0000>".
			i18nGettext ("Signing of the request __CSR_SERIAL__ failed. Aborting for security reasons.",
                         "__CSR_SERIAL__", $request->getSerial()).
			"</FONT>");
		print closeLogSection ();
		print closePage ();
		return undef;
	}

	## build new request object
	$text_req .= "\n".$tools->getFile ($tmpdir."/${$}_request.sig");
	if ($text_req eq $header.$text."\n") {
		print addPreLogLine ("<FONT COLOR=#FF0000>".
			i18nGettext (
              "Signing of the request __CSR_SERIAL__ failed. Cannot read signature from file __FILE__. Aborting for security reasons.",
              "__CSR_SERIAL__", $request->getSerial(),
			  "__FILE__", $tmpdir."/${$}_request.sig").
			"</FONT>");
		print closeLogSection ();
		print closePage ();
		return undef;
	}
	my $inform;
	if( $request->getParsed()->{HEADER}->{TYPE} =~ /(PKCS#10|IE)/i ) {
	        $inform = "PEM";
	} else {
        	$inform = $parsed_req->{HEADER}->{TYPE};
	}
	my $new_req = new OpenCA::REQ ( SHELL=>$cryptoShell, INFORM=>$inform, DATA=>$text_req );

	## update database
	if ( not $db->updateStatus (
			DATATYPE=>"PENDING_REQUEST",
			OBJECT => $new_req,
			NEWTYPE=>"APPROVED_REQUEST") ) {
		print addPreLogLine ("<FONT COLOR=#FF0000>".
			i18nGettext (
              "Cannot update the status of the request __CSR_SERIAL__. Aborting for security reasons.",
              "__CSR_SERIAL__", $request->getSerial()).
			"</FONT>");
		print closeLogSection ();
		print closePage ();
		return undef;
	}

	## remove .newCSR
	unlink ($id_dir."/acl/approvePendingCSR");

	##// add the request's serial to the list
	$tools->saveFile (FILENAME => $id_dir."/public/csrList",
			DATA       => $new_req->getSerial()."\n".$tools->getFile ($id_dir."/public/csrList"));

	print addPreLogLine (i18nGettext ("ID: __ID__ - automatically approved", "__ID__", $ID));

}

print addPreLogLine ("");
print addLogLine (gettext ("Batch processor finished"));
print closeLogSection ();

print closePage();

}

1;
