## OpenCA - Public Web-Gateway Command
## (c) 1999-2009 by Massimiliano Pala and OpenCA Group
##
##   File Name: startAutoCA
##       Brief: start Auto (On-Line) CA Daemon
##     Version: $Revision: 1.11 $
## Description: this script starts the On-Line CA Daemon
##  Parameters: 

use strict;

sub cmdStartAutoCA {

	my $pid = undef;
	my $status = undef;
	my $pidfile = undef;
	my $params = undef;

	## Parameters used by the issuing certificate process

        our ($query, $errno, $errval, $cryptoShell, $ca_token, $DEBUG);
        our (%AUTOCONF);

	my $db = undef;

	$DEBUG = 0;

	$pidfile = $AUTOCONF{"var_prefix"}."/tmp/openca_autoca.pid";
        $status = libGetPidProcessStatus ( $pidfile );

        if( $status gt 0 ) {
                generalError( "ERROR, the On-Line Daemon is already active!");
        } elsif ( not defined $status ) {
                generalError("Can not open $pidfile!");
        }

	if( not defined $ca_token ) {
		$ca_token = crypto_get_ca_token();
		if( not $ca_token ) {
			print STDERR "ERROR::Can not get the CA token!\n";
			generalError( "ERROR, Can not get the CA_TOKEN." .
				" Please check the token password.\n", 99 );
		}
	}

	$pid = fork();
	if( $pid < 0 ) {
		generalError( "ERROR, can not spawn new processes!\n" );
	} elsif ( $pid > 0 ) {
		if( not open( FD, ">$pidfile")) {
			generalError("Can not write to $pidfile!");
		} else {
			print FD "$pid";
			close( FD );
		}

		sleep( 3 );
		$query->param ('cmd', 'statusAutoCA' );
        	libExecuteCommand ();

	} else {
		# This is the main loop for the auto-ca options
		# while( 1 ) {
		close (STDOUT);

		$SIG{HUP} = \&closeAutoCA;

		# Load the parameters
		$params = startAutoCA_loadParams();

		if( not ($db = newConnectDB())) {
			print STDERR "autoCA::ERROR::Can not connect to DB!\n"
				if ( $DEBUG );
			exit(1);
		} else {
			print STDERR "AutoCA::Start()->DB connection Ok.\n"
				if ( $DEBUG );
		}
		$params->{DB} = $db;

		while( 1 ) {
			my $retVal = 0;

			if( $DEBUG ) {
			   print STDERR "On-Line CA::Cycle Start!\n";
			   print STDERR "On-Line CA::DB=>$db\n";
			   print STDERR "On-Line CA::CRYPTO=>$ca_token\n";
			};

			if( not $db->connect()) {
				print STDERR "On-Line CA::DB CONNECT ERROR::" . 
					$db->{errno} . " - " .
					$db->{errval} . "\n";
					next;
			} else {
				print STDERR "On-Line CA::DB CONNECT Ok\n"
					if( $DEBUG );
			}

			$params->{CA_TOKEN} = $ca_token;
			$params->{DB} = $db;
			$retVal = autoCAProcess( %{ $params } );

			$db->commit();

			if( not $db->disconnect()){
				print STDERR "On-Line CA::DB DISCONNECT ERR::". 
					$db->{errno} . " - " .
					$db->{errval} . "\n";
			} else {
				print STDERR "On-Line CA::DB DISCONNECT Ok\n"
					if( $DEBUG );
			} 

			sleep( 10 );

			# if( $retVal eq -666 ) {
			# 	closeAutoCA("Unrecoverable Error!");
			# 	exit(1);
			# }
		}
	}
}

sub autoCAProcess {

    ## get the parameters
    ## Get the parameters
    my $params	 = { @_ };

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

    our ($query, $errno, $errval, $cryptoShell, $ca_token, $DEBUG);

    if( $DEBUG ) {
      print STDERR "On-Line CA::autoCAProcess() started\n";
      print STDERR "On-Line CA::Params::CA_TOKEN=>" . $params->{CA_TOKEN} ."\n";
    };

    my $chainDir = getRequired('ChainDir');

    if( not $params ) {
    	print STDERR "On-Line CA::autoCAProcess()::Unrecoverable Error\n";
	return( 666 );
    }

    if( $DEBUG ) {
      print STDERR "On-LineCA::params::CA_TOKEN=>".$params->{CA_TOKEN}."\n";
    }
    
    my $tempDir = getRequired ('TempDir');

    ## loop
    ## there can never be a request 0
    my $key = 0;
    my $dataType = "APPROVED_REQUEST";
    my $maxItems = 100;

    if ( $DEBUG) {
    	print STDERR "On-Line CA::autoCAProcess()::Start Request Listing\n"
    };

    my $db = $params->{DB};

    my @list = $db->searchItems( DATATYPE => $dataType,
                                 ITEMS    => $maxItems );

    if ( $DEBUG ) {
    	print STDERR "On-Line CA::autoCAProcess()::Retrieved $#list requests\n"
    }

    while ( $request = pop ( @list ) ) {

    	print STDERR "On-Line CA::autoCAProcess()::got request ($request)\n"
		if( $DEBUG );

	$key = $request->getSerial();

    	print STDERR "On-Line CA::autoCAProcess()::got serial ($key)\n"
		if( $DEBUG );

	## check the requsted role
	if ( (not isIn("Any", $params->{role})) and
			( not isIn($request->getParsed()->{HEADER}->{ROLE},
				$params->{role})) ) {

		if( $DEBUG ) {
		    print STDERR "On-Line CA::autoCAProcess()::" .
                	i18nGettext ( "CSR __CSR_SERIAL__ ignored because the".
				" requested role is __ROLE__.\n",
				"__CSR_SERIAL__", $request->getSerial(),
				"__ROLE__", $request->getParsed()->{HEADER}->{ROLE});
		};
		next;
	} else {
		if ($DEBUG) {
		   print STDERR "ROLE::ANY = " . $params->{ra} . "\n";
		   print STDERR "ROLE::Matching (" .
			$request->getParsed()->{HEADER}->{ROLE} . ")\n";
		}
	}

	if ( (not isIn("Any", $params->{ra})) and
			(not isIn( $request->getParsed()->{HEADER}->{RA},
					$params->{ra})) ) {

		if( $DEBUG ) {
		    print STDERR "On-Line CA::autoCAProcess()::" .
                	i18nGettext ( "CSR __CSR_SERIAL__ ignored because the".
				" requested RA is __RA__.\n",
				"__CSR_SERIAL__", $request->getSerial(),
				"__RA__", $request->getParsed()->{HEADER}->{RA});
		};
		next;
	} else {
		if( $DEBUG ) {
		   print STDERR "RA::ANY = " . $params->{ra} . "\n";
		   print STDERR "RA::Matching (" .
			$request->getParsed()->{HEADER}->{RA} . ")\n";
		}
	}

	## is there a signature?
	$signature = libGetSignatureObject (OBJECT => $request);
	if( $DEBUG ) {
    		print STDERR "On-Line CA::autoCAProcess()::got " . 
						"signature ($signature)\n";
	}

	if ( (not $signature) and (isIn("1", $params->{reqsig})) ){

		if( $DEBUG ) {
			print STDERR "On-Line CA::autoCAProcess()::" .
                	    i18nGettext ( "CSR __CSR_SERIAL__ ignored " . 
				"because the".
				" signature is required.\n",
				"__CSR_SERIAL__", $request->getSerial());
		}

		next;

	} else {
		if( $DEBUG ) {
		   print STDERR "SIG:: Requirement Matching\n";
		}
	}

	if( not $signature ) {
		# If a signature on the request is required,
		# then we go to the next
		if ( $DEBUG ) {
    			print STDERR "On-Line CA::autoCAProcess()::no " . 
				"signature on request\n";
		}
	} else {
		##// load the signer's cert
		if ( $DEBUG ) {
    			print STDERR "On-Line CA::autoCAProcess()::request " .
						"is signed\n";
		};

		$operator_cert = libGetSignerCertificateDB (
						SIGNATURE => $signature);
		if (not $operator_cert) {
			if( $DEBUG ) {
			   print STDERR i18nGettext (
				  "CSR __CSR_SERIAL__ ignored because the " .
				  "signer's certificate is not loadable.\n",
                  		  "__CSR_SERIAL__", $request->getSerial()) ;
			}
			next;
		}

		## check role of signer
		if ( (not isIn("Any",$params->{ra})) and 
		       (not isIn($operator_cert->getParsed()->{HEADER}->{ROLE},
					$params->{operator} )) ) {
			if ( $DEBUG ) {
		 	   print STDERR i18nGettext (
				"CSR __CSR_SERIAL__ ignored because the ".
				"signer's role is __ROLE__.\n",
                 		"__CSR_SERIAL__", $request->getSerial(),
		 		"__ROLE__", $operator_cert->getParsed()->{HEADER}->{ROLE});
			};
			next;
		}
	}

	if( (not isIn("Any", $params->{keysize})) and
			(not isIn ($request->getParsed()->{HEADER}->{KEY_BITS},
					$params->{keysize}))) {
		if ( $DEBUG ) {
		   print STDERR i18nGettext (
			"CSR __CSR_SERIAL__ ignored because the " .
			"keysize is __SIZE__.\n",
			"__CSR_SERIAL__", $request->getSerial(),
			"__SIZE__", $request->getParsed()->{HEADER}->{KEY_BITS});
		};
		next;
	}

	if( (not isIn("Any", $params->{algor})) and
			(not isIn ($request->getParsed()->{HEADER}->{KEY_ALGORITHM},
					$params->{algor}))) {
		if( $DEBUG ) {
		    print STDERR i18nGettext (
			"CSR __CSR_SERIAL__ ignored because the " .
			"algorithm is __ALGOR__.\n",
			"__CSR_SERIAL__", $request->getSerial(),
			"__ALGOR__", $request->getParsed()->{HEADER}->{KEY_ALGORITHM});
		};
		next;
	}

	## issue certificate
	if ( $DEBUG ) {
    		print STDERR "On-Line CA::autoCAProcess()::Issuing " .
				"the certificate\n";
	}

	$cert = libIssueCertificate (
			KEY      => $request->getSerial(),
			DATATYPE => "APPROVED_REQUEST",
			CA_TOKEN => $params->{CA_TOKEN},
			DB	 => $db );

	if (not $cert) {
		our ( $errno, $errval );

		## There's been an error: what shall we do ?
		## Options to implement:
		## - Send an email to the administrator and put the
		##   request back onto the queue (pending reqs ?)
    		print STDERR "On-Line CA::autoCAProcess()::ERROR in " .
			"issuing the certificate\n";
		print STDERR "On-Line CA::autoCAProcess()::ERROR::" .
			"$errno" . "::" . "$errval\n";
	} else {
		if( $DEBUG ){
    			print STDERR "On-Line CA::autoCAProcess()::" .
				"certificate issued correctly\n";
			print STDERR "On-Line CA::autoCAProcess()::" .
				"certificate data::$cert\n";
			print STDERR "On-Line CA::autoCAProcess()::" . 
				"certificate data::" .  $cert->getPEM() . "\n";
		};

		if( not $db->commit()) {
			print STDERR "On-Line CA::autoCAProcess()::DBI " .
				"commit error (" . $db->{errno} . " - " . 
					$db->{errval} . ") !!!\n";
		} else {
			print STDERR "On-Line CA::autoCAProcess()::DBI " .
				"commit Ok\n" if ($DEBUG);
		}
	}
   }

   if ( $DEBUG ) {
   	print STDERR "On-Line CA::autoCAProcess()::finished cycling " .
		"through reqs\n"
   }

   # Here we finished scanning the current list of requests and we
   # just return OK

   return(1);
}

sub getParamsStartAutoCA {

    our ($query, $DEBUG, $self);

    my $result = undef;

    my $pidfile = $AUTOCONF{"var_prefix"}."/tmp/openca_autoca.pid";
    my $status = libGetPidProcessStatus ( $pidfile );

    if( $status gt 0 ) {
	return undef;
    };

    if (not $_[0]) {

	my %labels = undef;

	my $params = startAutoCA_loadParams();

        ## set values
	my $html_operator = $query->newInput (
				-intype => 'scrolling_list',
				-name => 'operator',
				-regx=>'*',
				-default=> $params->{'operator'},
				-size=>5,
				-multiple=>'true',
				-values=>[ gettext('Any'), loadRoles()],
				-attributes => undef );

	my $html_reqsig = $query->newInput (
					-regx=>'LETTERS',
					-intype=>'checkbox',
					-name=>'reqsig',
					-value=> '1',
					-class=>'checkbox',
					-label=> '',
					-checked=>$params->{'reqsig'} );

	my $html_role = $query->newInput (
					-regx=>'*',
					-intype=>'scrolling_list',
					-name=>'role',
					-size=>5,
					-multiple=>'true',
					-default=> $params->{'role'},
					-values=>[gettext('Any'), loadRoles()]);

	my $html_loa = $query->newInput (
					-regx=>'*',
					-intype=>'scrolling_list',
					-name=>'loa',
					-size=>5,
					-multiple=>'true',
					-default=> $params->{'loa'},
					-values=>[gettext('Any'), loadLoa()]);

	my $html_ra = $query->newInput (
					-regx=>'LETTERS',
					-intype=>'scrolling_list',
					-name=>'ra',
					-size=>5,
					-multiple=>'true',
					-default=>$params->{'ra'},
					-values=>[gettext('Any'), 
						getRequiredList("RegistrationAuthority")]);

	%labels = ( 'Any' => gettext('Any'),
		    "224" => "224 (ecdsa)", "256" => "256 (ecdsa)",
		    "521" => "521 (ecdsa)", "1024" => "1024 (rsa/dsa)",
		    "2048" => "2048 (rsa/dsa)", "4096" => "4096 (rsa/dsa)",
		    "8192" => "8192 (rsa/dsa)" );

	my $html_keysize = $query->newInput (
					-regx=>'LETTERS',
					-intype=>'scrolling_list',
					-name=>'keysize',
					-size=>3,
					-multiple=>'true',
					-default=> $params->{'keysize'},
					-labels=>\%labels,
					-values=>['Any', 
						"224", "256", "521", "1024",
						"2048", "4096", "8192" ]);

	my $html_algor = $query->newInput (
					-regx=>'LETTERS',
					-intype=>'scrolling_list',
					-name=>'algor',
					-size=>3,
					-multiple=>'true',
					-default=> $params->{'algor'},
					-values=>[gettext('Any'), 
						'rsa', 'dsa', 'ecdsa' ]);

        $result = "<table class=\"getParams\">\n";
        $result .= "<tr><td colspan=\"2\">\n";
	$result .= "<center><div style='font-size: 120%;'><h3>" . 
		       gettext("Auto Certificate Issuing System")."</h3>" .
		   "</div></center>";

	$result .=  "<div class=\"description\" style='margin: 10px;'>" .
		    gettext (
                    "You can filter requests based on which Registration " .
		    "Authority has approved the request. The following " .
		    "information will be used by the Auto CA system to " .
		    "process only the requests that match all of the " .
		    "requirements you set here." .
		    "Remeber that although the configuration options are ".
		    "stored on your system, if the OpenCA server is " .
		    "rebooted you will need to activate the system again."
		    ) .
		    "</div>";
        $result .= "</td></tr>\n";

        $result .= "<tr><td colspan=\"2\">\n";
	$result .= "<center><h3>" . 
		       gettext("Request Signature Requirements")."</h3></div>" .
		   "</center>";
        $result .= "</td></tr>\n";

	$result .= "<tr><td colspan=\"2\">";
	$result .=  "<div class=\"description\" style='margin: 10px;'>" .
		    gettext (
                    "Please provide the information about the required ".
		    "approval process (e.g., if the request has to be " .
		    "signed, by which Registration Authority and by which " .
		    "RA Operator's role). " .
		    "Keep in mind that requests from the Authenticated " .
		    "request form, as all the new requests, are not signed, ".
		    "therefore you need to disable the Process (Signed) " .
		    "Requests Only check button." 
		     ) .
		    "</div><br />"; 
        $result .= "</td></tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Registration Authoritie(s)")."</td>\n".
                   "    <td>".$html_ra."</td>\n".
                   "  </tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Operator's role(s)")."</td>\n".
                   "    <td>".$html_operator."</td>\n".
                   "  </tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Processed (Signed) Requests Only")."</td>\n".
                   "    <td>" . $html_reqsig . "</td>\n".
                   "  </tr>\n";

	$result .= "<tr><td colspan=\"2\">";
	$result .= "<br /><center><h3>".
			gettext ("Requests Details") . "</h3>" .
		   "</div></center></td></tr>";

	$result .= "<tr><td colspan=\"2\">";
	$result .=  "<div class=\"description\" style='margin: 10px;'>" .
		    i18nGettext (
                    "The following information regard the technical details " .
		    "of the requests that will be processed. Keep in mind " .
		    "that all of the requirements must be met, therefore " .
		    "__A_HREF__" .
		    "please review the Level of Assurance (LOA) configuration ".
		    "__A_HREF_CLOSE__" .
		    "of your system." .
		    "To further restrict the requirements from the LOA you " .
		    "can select to automatically issue certificates only for ".
		    "a subset of the supported algorithms by selecting them " .
		    "from the menus." , 
			"__A_HREF__", "<a href=\"$self?cmd=viewLoas\" >",
			"__A_HREF_CLOSE__", "</a>"
		    ) .
		    "</div><br />";
	$result .= "</td></tr>";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Requested role(s)")."</td>\n".
                   "    <td>".$html_role."</td>\n".
                   "  </tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Level of Assurance(s)")."</td>\n".
                   "    <td>".$html_loa."</td>\n".
                   "  </tr>\n";

	$result .= "<tr><td colspan=\"2\">";
	$result .= "<br /><center><h3>".
			gettext("Accepted Algorithms and Key Sizes") . "</h3>" .
		   "</div></center></td></tr>";

	$result .= "<tr><td colspan=\"2\">";
	$result .=  "<div class=\"description\" style='margin: 10px;'>" .
		    gettext (
                    "You can further restrict the accepted algorithms and " .
		    "key sizes here. Keep in mind that keysizes bigger or " .
		    "equal to 1024 bits are for RSA and DSA algorithms, " .
		    "while keysizes ranging from 112 to 521 bits are for " .
		    "ECDSA (Elliptic Curves) only." 
		    ) .
		    "</div><br />";
        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Accepted Algorithms(s)")."</td>\n".
                   "    <td>".$html_algor."</td>\n".
                   "  </tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Accepted Key Size(s)")."</td>\n".
                   "    <td>".$html_keysize."</td>\n".
                   "  </tr>\n";

        $result .= "</table>\n";
    } else {
	startAutoCA_saveParams();
    }

    return $result;
};

sub closeAutoCA {

	my $reason = shift;
	my $statusfile = undef;

	our (%AUTOCONF);

	$statusfile = $AUTOCONF{"var_prefix"}."/tmp/openca_autoca_status.txt";

	if( ($reason eq "HUP") or ($reason eq "") ) {
		$reason = gettext ( "Process Stopped from CA Interface" );
	}

	# if( $db ) {
	# 	$db->disconnect();
	#}

	# Unrecoverable Error
	if( not open( FD, ">$statusfile")) {
		exit(1);
	} else {
		print FD "$reason";
		close( FD );
	}
	exit(1);
};

sub startAutoCA_saveParams {

	my $ret = undef;

	our ( $query, %AUTOCONF );

	my $conf = $AUTOCONF{"var_prefix"}."/db/openca_autoca.cnf";

	return libSaveCgiParams ( $conf );

	return ( $ret );
}

sub startAutoCA_loadParams {

	my $ret = undef
	my $savedQuery = undef;
	my $defaults = undef;

	$defaults->{'operator'} = [ gettext('Any') ];
	$defaults->{'ra'} = [ gettext('Any') ];
	$defaults->{'reqsig'} = '1';
	$defaults->{'role'} = [ gettext('Any') ];
	$defaults->{'loa'} = [ gettext('Any') ];
	$defaults->{'keysize'} = [ gettext('Any') ];
	$defaults->{'algor'} = [ gettext('Any') ];

	our ( $query, %AUTOCONF );

	my $conf = $AUTOCONF{"var_prefix"}."/db/openca_autoca.cnf";

	return libLoadCgiParams ( $conf, $defaults );

}

sub isIn {

        my $val = shift;
        my $list = shift;
	my @foo = undef;

	if ( ref($list) =~ /ARRAY/ ) {
                @foo = @{ $list };
        } else {
                @foo = split( "\0",$list );
        };

        if ( grep ( /$val/, @foo )) {
                return 1;
        }
	return undef;
}

1;
