#!/usr/bin/perl # # --- BEGIN COPYRIGHT BLOCK --- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright (C) 2007-2010 Red Hat, Inc. # All rights reserved. # --- END COPYRIGHT BLOCK --- # use strict; use warnings; use Getopt::Long qw(GetOptions); ############################################################## # This script is used to remove an existing PKI instance. # # To execute: # # ./pkiremove -pki_instance_root= # Instance root # # directory destination # # -pki_instance_name= # Unique PKI subsystem # # instance name # # (e. g. - pki-pki1) # # [-token_pwd=] # Password of token containing # # subsystem certificate # # [-force] # Don't ask any # # questions # ############################################################## ############################################################## # Execution Check ############################################################## # Check to insure that this script's original # invocation directory has not been deleted! my $cwd = `/bin/pwd`; chomp $cwd; if (!$cwd) { emit("Cannot invoke '$0' from non-existent directory!\n", "error"); exit 255; } ############################################################## # Environment Variables ############################################################## # untaint called subroutines if (($^O ne 'Windows_NT') && ($^O ne 'MSWin32')) { $> = $<; # set effective user ID to real UID $) = $(; # set effective group ID to real GID $ENV{'PATH'} = '/bin:/usr/bin'; $ENV{'ENV'} = '' if !defined($ENV{'ENV'}); } ############################################################## # Command-Line Variables ############################################################## my $ARGS = ($#ARGV + 1); ############################################################## # Shared Common Perl Data and Subroutines ############################################################## use lib "/usr/share/pki/scripts"; use pkicommon; # make -w happy by suppressing warnings of Global variables used only once my $suppress = ""; $suppress = $default_file_permissions; ############################################################## # Local Constants ############################################################## my $semanage = "/usr/sbin/semanage"; ############################################################## # Local Data Structures ############################################################## ############################################################## # Local Variables ############################################################## my $pki_instance_root = undef; my $pki_instance_name = undef; my $force = 0; my $token_pwd = ""; my $conf_file = undef; my $pki_instance_path = undef; my $subsystem_type = undef; # PKI init script variables my $pki_registry_initscript = undef; my $pki_registry_initscript_command = undef; # PKI registry variables my $pki_registry_subsystem_path = undef; #systemd specific variables my $use_systemd = 0; my $pki_instance_systemd_service_name = undef; ############################################################## # Platform-Dependent Data Initialization ############################################################## if ($^O eq "linux") { if (is_Fedora() && (fedora_release() >= 16)) { $use_systemd = 1; } } else { emit("Unsupported platform '$^O'!\n", "error"); exit 255; } ############################################################## # Local Data Initialization ############################################################## ############################################################## # PKI Instance Removal Subroutines ############################################################## # no args # no return value sub usage { print STDOUT <<'EOF'; Usage: pkiremove -pki_instance_root= # Instance root # directory # destination -pki_instance_name= # Unique PKI # subsystem # instance name # (e. g. - pki-pki1) # [-token_pwd=] # Password for token containing subsystem cert. [-force] # Don't ask any questions [-verbose] # Display detailed information. May be specified multiple times, # each time increasing the verbosity level. [-dry_run] # Do not perform any actions. # Just report what would have been done. Example: pkiremove -pki_instance_root=/var/lib -pki_instance_name=pki-tps IMPORTANT: Must be run as root! IMPORTANT: pkiremove should only be used to remove instances which were created using pkicreate. Instances created using pkispawn should be removed using pkidestroy. EOF return; } sub update_domain { my $sport; my $ncsport; my $sechost; my $httpport; my $seceeport; my $secagentport; my $secadminport; my $adminsport; my $agentsport; my $secselect; my $subsystemnick; my $machinename; my $typeval; my $url; get_cs_cfg($conf_file, {"service.machineName" => \$machinename, "service.securityDomainPort" => \$sport, "service.non_clientauth_securePort" => \$ncsport, "securitydomain.host" => \$sechost, "securitydomain.httpport" => \$httpport, "securitydomain.httpseeport" => \$seceeport, "securitydomain.httpsagentport" => \$secagentport, "securitydomain.httpsadminport" => \$secadminport, "securitydomain.select" => \$secselect, "pkicreate.admin_secure_port" => \$adminsport, "cs.type" => \$typeval, "pkicreate.agent_secure_port" => \$agentsport}); my $subsystemnick_param = lc($typeval) . ".cert.subsystem.nickname"; get_cs_cfg($conf_file, {$subsystemnick_param => \$subsystemnick}); # NOTE: Don't check for the existence of $httpport, as this will # be undefined for a Security Domain that has been migrated! if ((!defined($sechost)) || (!defined($seceeport)) || (!defined($secagentport)) || (!defined($secadminport))) { print(STDOUT "No security domain defined.\nIf this is an unconfigured instance, then that is OK.\n" . "Otherwise, manually delete the entry from the security domain master.\n"); return; } die "Subsystem nickname not defined" if (!defined($subsystemnick)); if (!defined($adminsport)) { $adminsport = ""; } if (!defined($agentsport)) { $agentsport = ""; } if (!defined($ncsport)) { $ncsport = ""; } (my $token_name, my $nick) = split(/:/, $subsystemnick, 2); if ((!defined($nick)) || ($nick eq "")) { $token_name = "internal"; } if ($secselect ne "new") { # This is not a domain master, so we need to update the master print(STDOUT "Contacting the security domain master to update the security domain\n"); my $listval = $subsystem_type . "List"; my $urlheader = "https://" . $sechost . ":" . $seceeport; my $urlagentheader = "https://" . $sechost . ":" . $secagentport; my $urladminheader = "https://" . $sechost . ":" . $secadminport; my $updateURL = "/ca/agent/ca/updateDomainXML"; if ($token_pwd eq "") { my $pwfile = $pki_instance_path . "/conf/password.conf"; if (-r $pwfile) { open(DAT, $pwfile) or die "Could not open password.conf file to read token password."; my @pw_data=; foreach my $line (@pw_data) { chomp($line); if (($typeval eq "CA") || ($typeval eq "KRA") || ($typeval eq "OCSP") || ($typeval eq "TKS")) { (my $varname, my $valname) = split(/=/, $line); if ($varname eq "hardware-$token_name") { $token_pwd = $valname; } if ($varname eq "$token_name") { $token_pwd = $valname; } } else { # TPS, RA (my $varname, my $valname) = split(/:/, $line); if ($varname eq $token_name) { $token_pwd = $valname; } if ($varname eq "hardware-$token_name") { $token_pwd = $valname; } } } close($pwfile); } } while ($token_pwd eq "") { $token_pwd = prompt( "No password found for $token_name. What is the password for this token?"); } my $params = "name=$pki_instance_name" . "&type=$typeval" . "&list=$listval" . "&host=$machinename" . "&sport=$sport" . "&ncsport=$ncsport" . "&adminsport=$adminsport" . "&agentsport=$agentsport" . "&operation=remove"; #update domainXML my $cmd = `/usr/bin/sslget -d \"$pki_instance_path/alias\" -p \"$token_pwd\" -v -n \"$subsystemnick\" -r \"$updateURL\" -e \"$params\" $sechost:$secagentport 2>&1`; $cmd =~ /\(.*?)\<\/Status\>/; $cmd = $1; die ("Security Domain returns non-zero status for updateDomainXML.") if ($cmd ne "0"); } } sub remove_fcontext { my ($fcontext, $fname, $ftype, $cmd_ref) = @_; emit(sprintf("remove_fcontext(%s)\n", join(", ", @_)), "debug"); my $tmp = `$semanage fcontext -l -C |grep $fcontext |grep $fname |wc -l`; chomp $tmp; if ($tmp eq "0" ) { emit("File context $fcontext for $fname defined in policy, not deleted", "debug"); return 0; } if ($ftype eq "f") { $$cmd_ref .= "fcontext -d -t $fcontext -f -- $fname\n"; } else { $$cmd_ref .= "fcontext -d -t $fcontext $fname\n"; } } sub get_selinux_fcontexts { my ($cmd_ref) = @_; my $setype = "pki_" . $subsystem_type; my $default_instance_name = "pki-" . $subsystem_type; my $default_instance_root = "/var/lib"; my $default_log_path = "/var/log/" . $default_instance_name; my $default_conf_path = "/etc/" . $default_instance_name; my $log_path = "$pki_instance_path/logs"; my $conf_path = "$pki_instance_path/conf"; my $ftype; my $java_component = 0; if (($subsystem_type eq $CA) || ($subsystem_type eq $KRA) || ($subsystem_type eq $OCSP) || ($subsystem_type eq $TKS)) { $java_component=1; } if (-l $log_path) { $log_path = readlink $log_path; }; if (-l $conf_path) { $conf_path = readlink $conf_path; }; # For backwards compatibility, support removal of instances # which use the legacy start/stop implementation if (entity_exists("$default_initscripts_path/$pki_instance_name")) { # remove context for "$default_initscripts_path/$pki_instance_name" if ($pki_instance_name ne $default_instance_name) { remove_fcontext($setype . "_script_exec_t", "/etc/rc\\.d/init\\.d/$pki_instance_name", "f", $cmd_ref); } } # remove context for $pki_instance_root/$pki_instance_name if (($pki_instance_name ne $default_instance_name) || ($pki_instance_root ne $default_instance_root)) { remove_fcontext($setype . "_var_lib_t", "\"$pki_instance_root/$pki_instance_name(/.*)?\"", "a", $cmd_ref); if (! $java_component) { remove_fcontext($setype . "_exec_t", "\"${pki_instance_root}/{$pki_instance_name}/${pki_instance_name}\"", "a", $cmd_ref); } } # remove context for /var/run/$pki_instance_name.pid if (($java_component) && ($pki_instance_name ne $default_instance_name)) { remove_fcontext($setype . "_var_run_t", "/var/run/$pki_instance_name" . '.pid', "f", $cmd_ref); } # remove context for $log_path if ($log_path ne $default_log_path) { remove_fcontext($setype . "_log_t", "\"$log_path(/.*)?\"", "a", $cmd_ref); } # remove context for $conf_path if ($conf_path ne $default_conf_path) { remove_fcontext($setype . "_etc_rw_t", "\"$conf_path(/.*)?\"", "a", $cmd_ref); } } sub get_selinux_ports { my ($cmd_ref) = @_; my $status; my $semanage = "/usr/sbin/semanage"; my $secure_port; my $non_clientauth_secure_port; my $unsecure_port; my @ports = (); get_cs_cfg($conf_file, {"service.securePort" => \$secure_port, "service.non_clientauth_securePort" => \$non_clientauth_secure_port, "service.unsecurePort" => \$unsecure_port}); if (($subsystem_type eq $CA) || ($subsystem_type eq $KRA) || ($subsystem_type eq $OCSP) || ($subsystem_type eq $TKS)) { use XML::LibXML; my $parser = XML::LibXML->new(); my $config = $parser->parse_file($pki_instance_path . "/conf/server.xml") or die "Could not read XML from server.xml to determine ports."; my $root = $config->getDocumentElement; my $i = 0; foreach my $port ($root->findnodes('//@port')) { $ports[$i] = $port->getValue(); $i++; } } else { # TPS, RA my $i =0; if (defined $secure_port) { $ports[$i] = $secure_port; $i++; } if (defined $non_clientauth_secure_port) { $ports[$i] = $non_clientauth_secure_port; $i++; } if (defined $unsecure_port) { $ports[$i] = $unsecure_port; $i++; } } print(STDOUT "\n"); foreach my $port (@ports) { my $setype = "pki_" . $subsystem_type . "_port_t"; my $tmp = `$semanage port -l -C |grep $setype |grep $port | wc -l`; chomp $tmp; if ($tmp eq "0") { emit("Port context $setype for $port defined in policy, not deleting", "debug"); } else { $$cmd_ref .= "port -d -t $setype -ptcp $port\n"; } } } # Return 1 if success, 0 if failure sub remove_instance { my ($result, $confirm, $install_info); $confirm = "Y"; $result = 1; print(STDOUT "PKI instance Deletion Utility cleaning up instance ...\n\n"); ASK_AGAIN: if (!$force) { $confirm = prompt("You have elected to remove the instance " . "installed in $pki_instance_path.\n" . "Are you sure (Y/N)? "); } if ($confirm eq "N" || $confirm eq "n") { return 1; } elsif ($confirm ne "Y" && $confirm ne "y") { goto ASK_AGAIN; } $install_info = read_install_info_from_dir($pki_instance_path); if (!defined($install_info)) { emit("Can't remove instance, installation manifest does not exist!", "error"); return $result; } eval { update_domain(); } if !$dry_run; # FIXME so update_domain shows what it would do warn "Error updating security domain: " . $@ if $@; if (($^O eq "linux") && (is_Fedora() || (is_RHEL() && (! is_RHEL4())))) { my $semanage_cmds = ""; eval { get_selinux_ports(\$semanage_cmds); }; warn "Error getting selinux ports: " . $@ if $@; eval { get_selinux_fcontexts(\$semanage_cmds); }; warn "Error getting selinux file contexts: " . $@ if $@; print STDOUT "Removing selinux contexts\n"; if ($semanage_cmds ne "") { emit("Executing selinux commands in batch mode.\n", "debug"); if (! $dry_run) { if (! run_command("$semanage -S targeted -i - " . '<< _EOF' . "\n$semanage_cmds\n" . '_EOF' . "\n")) { emit("Error executing selinux batch commands\n", "error"); } } } else { emit("No selinux contexts need to be removed. No need to run semanage. \n"); } } $pki_registry_initscript = get_registry_initscript_name($subsystem_type); # Shutdown this instance if ($^O eq "linux") { if ($use_systemd) { $pki_instance_systemd_service_name = "${pki_registry_initscript}\@${pki_instance_name}.service"; $pki_registry_initscript_command = "/bin/systemctl stop $pki_instance_systemd_service_name"; } else { if (entity_exists("$default_initscripts_path/$pki_instance_name")) { $pki_registry_initscript_command = "/sbin/service $pki_instance_name stop"; } else { $pki_registry_initscript_command = "/sbin/service $pki_registry_initscript stop $pki_instance_name"; } } } else { emit("Unsupported platform '$^O'!\n", "error"); exit 255; } run_command($pki_registry_initscript_command); if (!$use_systemd) { # De-register this instance with "chkconfig" if ($^O eq "linux") { if (entity_exists("$default_initscripts_path/$pki_instance_name")) { # De-register this instance with '/sbin/chkconfig' print(STDOUT "Removing '$pki_instance_name' from chkconfig.\n"); deregister_pki_instance_with_chkconfig($pki_instance_name); } } } print(STDOUT "\n"); # Remove all installed files and directories. $result = 0 if !uninstall($install_info); if ($use_systemd) { run_command("/bin/systemctl --system daemon-reload"); } print(STDOUT "\n"); return $result; } ############################################################## # Main Program ############################################################## # no args # return 1 - success, or # return 0 - failure sub main { chdir("/tmp"); my $result = 0; print(STDOUT "PKI instance Deletion Utility ...\n\n"); # On Linux/UNIX, insure that this script is being run as "root". $result = check_for_root_UID(); if (!$result) { usage(); exit 255; } # Check for a valid number of command-line arguments. if ($ARGS < 2) { emit("$0: Insufficient arguments!", "error"); usage(); exit 255; } # Parse command-line arguments. $result = GetOptions("pki_instance_root=s" => \$pki_instance_root, "pki_instance_name=s" => \$pki_instance_name, "token_pwd=s" => \$token_pwd, "verbose+" => \$verbose, "dry_run" => \$dry_run, "force" => \$force); # Always disallow root to be the pki_instance_root. if ($pki_instance_root eq "/") { emit("$0: Don't even think about making root " . "the pki_instance_root!", "error"); usage(); exit 255; } $pki_instance_root = normalize_path($pki_instance_root); # Check for valid content of command-line arguments. if (!$pki_instance_root) { emit("$0: Must have value for -pki_instance_root!", "error"); usage(); exit 255; } if (!$pki_instance_name) { emit("$0: The instance ID of the PKI instance " . "to be removed is required!", "error"); usage(); exit 255; } $pki_instance_path = "${pki_instance_root}/${pki_instance_name}"; if (!directory_exists($pki_instance_path)) { emit("$0: Target directory $pki_instance_path " . "is not a legal directory.", "error"); usage(); exit 255; } # Capture uninstall information in a log file, always overwrite this file. # When removing an instance it's never a fatal error if the logfile # cannot be created. my $logfile = "/var/log/${pki_instance_name}-uninstall.log"; open_logfile($logfile, $default_file_permissions); emit("Capturing installation information in $logfile.\n"); if ($verbose) { emit(" verbose mode ENABLED (level=$verbose)\n"); } if ($dry_run) { emit(" dry run mode ENABLED, system will not be modified, log to $logfile\n"); print STDOUT "dry run mode ENABLED, system will not be modified\n"; } emit(" pki_instance_root $pki_instance_root\n"); emit(" pki_instance_name $pki_instance_name\n"); emit(" pki_instance_path $pki_instance_path\n"); $conf_file = $pki_instance_path . "/conf/CS.cfg"; $subsystem_type = get_cs_cfg($conf_file, "cs.type"); if (!defined($subsystem_type)) { emit("Could not determine the subsystem type from the file \"$conf_file\"\n", "error"); exit 1; } $subsystem_type = lc($subsystem_type); # Remove the specified instance $result = remove_instance(); if ($result != 1) { exit 255; } # Establish PKI subsystem-level registry $pki_registry_subsystem_path = "$pki_registry_path/$subsystem_type"; # If empty, remove the PKI subsystem-level registry if (directory_exists($pki_registry_subsystem_path)) { if (is_directory_empty($pki_registry_subsystem_path)) { remove_directory($pki_registry_subsystem_path); } } # If empty, remove the PKI-level registry if (directory_exists($pki_registry_path)) { if (is_directory_empty($pki_registry_path)) { remove_directory($pki_registry_path); } } if ($dry_run) { print STDOUT "dry run mode ENABLED, system was not modified\n"; } return $result; } ############################################################## # PKI Instance Removal ############################################################## main(); exit 0;