mirror of
https://github.com/zerotier/edge.git
synced 2026-05-22 16:25:05 -07:00
7808 lines
194 KiB
Perl
Executable File
7808 lines
194 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
#
|
|
# Copyright 2005-2014 SPARTA, Inc. All rights reserved. See the COPYING
|
|
# file distributed with this software for details.
|
|
#
|
|
#
|
|
# rollerd
|
|
#
|
|
# This script manages the key rollover process.
|
|
#
|
|
# ZSK rollover uses the Pre-Publish Method of rollover and takes
|
|
# place in four phases:
|
|
# - wait until it's time to perform a ZSK rollover
|
|
# - sign the zone with the KSK and Published ZSK
|
|
# - wait for old zone data to expire from caches
|
|
# - sign the zone with the new Current ZSK key
|
|
#
|
|
# KSK rollover uses the Double Signature Method of rollover and takes
|
|
# place in seven phases:
|
|
# - wait for cache data to expire
|
|
# - generate a new (published) KSK
|
|
# - wait for the old DNSKEY RRset to expire from caches
|
|
# - roll the KSKs
|
|
# - transfer new keyset to the parent
|
|
# (currently, this is done manually)
|
|
# - wait for parent to publish the new DS record
|
|
# (currently, this is done manually)
|
|
# - reload the zone
|
|
#
|
|
# See the pod for much more information.
|
|
#
|
|
|
|
#
|
|
# If we're executing from a packed environment, make sure we've got the
|
|
# library path for the packed modules.
|
|
#
|
|
BEGIN
|
|
{
|
|
if($ENV{'PAR_TEMP'})
|
|
{
|
|
unshift @INC, ("$ENV{'PAR_TEMP'}/inc/lib");
|
|
}
|
|
}
|
|
|
|
|
|
use strict;
|
|
|
|
use Cwd;
|
|
use Getopt::Long qw(:config no_ignore_case_always);
|
|
|
|
use Net::DNS::SEC::Tools::dnssectools;
|
|
use Net::DNS::SEC::Tools::conf;
|
|
use Net::DNS::SEC::Tools::defaults;
|
|
use Net::DNS::SEC::Tools::keyrec;
|
|
use Net::DNS::SEC::Tools::rolllog;
|
|
use Net::DNS::SEC::Tools::rollmgr;
|
|
use Net::DNS::SEC::Tools::rollrec;
|
|
use Net::DNS::SEC::Tools::timetrans;
|
|
use Net::DNS::SEC::Tools::tooloptions;
|
|
use Net::DNS::SEC::Tools::BootStrap;
|
|
|
|
use POSIX qw(setsid);
|
|
use POSIX qw(setuid);
|
|
|
|
#
|
|
# Version information.
|
|
#
|
|
my $NAME = "rollerd";
|
|
my $VERS = "$NAME version: 2.1.0";
|
|
my $DTVERS = "DNSSEC-Tools Version: 2.2.3";
|
|
|
|
#######################################################################
|
|
|
|
my $ME = "rollerd";
|
|
|
|
#
|
|
# Some path variables to be set from the config file.
|
|
#
|
|
my $dtconfig; # DNSSEC-Tools configuration file.
|
|
my $rndc; # BIND name server control program.
|
|
my $rrchk; # Rollrec file checking program.
|
|
my $zonesigner; # Zone-signing program.
|
|
|
|
my $rndcopts; # Options for rndc.
|
|
|
|
my $PWD = '/bin/pwd';
|
|
|
|
my $DEFAULT_NAP = 60;
|
|
|
|
##########################################
|
|
#
|
|
# Method selected for calculating rollover times.
|
|
#
|
|
|
|
my $RM_ENDROLL = 1; # Calculate from end of last roll.
|
|
my $RM_KEYGEN = 2; # Calculate from last key generation.
|
|
my $RM_STARTROLL = 3; # Calculate from start of last roll. (NYI)
|
|
|
|
my $krollmethod = $RM_ENDROLL; # Rollover calculation to use for KSKs.
|
|
my $zrollmethod = $RM_ENDROLL; # Rollover calculation to use for ZSKs.
|
|
|
|
##########################################
|
|
#
|
|
# Data required for command line options.
|
|
#
|
|
|
|
my $rollrecfile; # Rollrec file to be managed.
|
|
|
|
my %dtconf; # DNSSEC-Tools config file.
|
|
my $DT_LOADZONE = 'roll_loadzone';
|
|
my $DT_LOGFILE = 'roll_logfile';
|
|
my $DT_LOGLEVEL = 'roll_loglevel';
|
|
my $DT_LOGTZ = 'log_tz';
|
|
my $DT_RNDCOPTS = 'rndc-opts';
|
|
my $DT_SLEEP = 'roll_sleeptime';
|
|
my $DT_USERNAME = 'roll_username';
|
|
|
|
my $OPT_ALWAYSSIGN = 'alwayssign';
|
|
my $OPT_AUTOSIGN = 'autosign';
|
|
my $OPT_DIR = 'directory';
|
|
my $OPT_DISPLAY = 'display';
|
|
my $OPT_DTCONF = 'dtconfig';
|
|
my $OPT_FOREGROUND = 'foreground';
|
|
my $OPT_HELP = 'help';
|
|
my $OPT_LOGFILE = 'logfile';
|
|
my $OPT_LOGLEVEL = 'loglevel';
|
|
my $OPT_LOGTZ = 'logtz';
|
|
my $OPT_NORELOAD = 'noreload';
|
|
my $OPT_PARAMS = 'parameters';
|
|
my $OPT_PIDFILE = 'pidfile';
|
|
my $OPT_REALM = 'realm';
|
|
my $OPT_RRFILE = 'rrfile';
|
|
my $OPT_SINGLERUN = 'singlerun';
|
|
my $OPT_SLEEP = 'sleep';
|
|
my $OPT_USERNAME = 'username';
|
|
my $OPT_VERBOSE = 'verbose';
|
|
my $OPT_VERSION = 'Version';
|
|
my $OPT_ZONESIGNER = 'zonesigner';
|
|
my $OPT_ZSARGS = 'zsargs';
|
|
|
|
my %opts = (); # Filled option array.
|
|
my @opts =
|
|
(
|
|
"rrfile=s", # Rollrec file.
|
|
"directory=s", # Execution directory.
|
|
"display", # Use output GUI.
|
|
"logfile=s", # Log file.
|
|
"loglevel=s", # Logging level.
|
|
"logtz=s", # Logging timezone.
|
|
"noreload", # Don't reload zone files.
|
|
"pidfile=s", # pid storage file.
|
|
"dtconfig=s", # dnssec-tools config file to use.
|
|
"sleep=i", # Sleep amount (in seconds.)
|
|
"parameters", # Display the parameters and exit.
|
|
"autosign!", # Autosign flag.
|
|
"singlerun", # Single run: process everything once.
|
|
"foreground", # Run in the foreground; don't fork.
|
|
"alwayssign", # Always sign when running in singlerun.
|
|
"username=s", # User name for which to run as.
|
|
"realm=s", # Realm we're running in.
|
|
"help", # Give a usage message and exit.
|
|
"verbose", # Verbose output.
|
|
"Version", # Display the version number.
|
|
"zonesigner=s", # Location of zonesigner executable.
|
|
"zsargs=s", # Arguments for zonesigner.
|
|
);
|
|
|
|
#
|
|
# Flag values for the various options. Variable/option connection should
|
|
# be obvious.
|
|
#
|
|
my $alwayssign = 0; # Always sign the zone in -singlerun.
|
|
my $autosign = 0; # Autosign updated zones.
|
|
my $dtcf; # DNSSEC-Tools configuration file.
|
|
my $foreground = 0; # Run in the foreground.
|
|
my $logfile; # Log file.
|
|
my $loglevel; # Logging level.
|
|
my $loglevel_save; # Saved logging level.
|
|
my $logtz; # Logging timestamp.
|
|
my $zoneload = 1; # Zone-reload flag.
|
|
my $pidfile; # Pid storage file.
|
|
my $realm; # Our realm.
|
|
my $singlerun = 0; # Single run only.
|
|
my $sleep_override = 0; # Sleep-override flag.
|
|
my $sleepcnt; # Time we've slept so far.
|
|
my $sleeptime; # Sleep interval in seconds.
|
|
my $username; # User name we'll change to.
|
|
my $gzsargs = ''; # Global zsargs for zonesigner.
|
|
my $verbose = 0; # Verbose option.
|
|
|
|
my $display = 0; # Do display processing.
|
|
|
|
my $boottime = gmtime(); # Timestamp of rollerd's start time.
|
|
|
|
my $MIN_SLEEP = 10; # Minimum time rollerd will sleep.
|
|
|
|
my $curdir; # Directory.
|
|
my $keyarch; # Key-archive program.
|
|
my $packed = 0; # Flag indicating if running packed.
|
|
my $xqtdir; # Execution directory.
|
|
|
|
#######################################################################
|
|
|
|
my $EVT_FULLLIST = 1; # Full list is run every N seconds.
|
|
my $EVT_QUEUE_SOON = 2; # Queues, with "soon" events.
|
|
|
|
#
|
|
# "full list" queue processing is the classic rollerd method of handling its
|
|
# queue. Every N seconds, the entire queue of zones is scanned to see if any
|
|
# rollover events must be handled.
|
|
#
|
|
# "soon" queue processing is experimental. It maintains a sub-queue of the
|
|
# rollover events that must be handled soon. Rather than processing the full
|
|
# queue of managed zones every N seconds, the "soon queue" is handled as the
|
|
# events occur.
|
|
#
|
|
# "soon" processing is still being tested and should *not* be considered
|
|
# reliable! (yet...)
|
|
#
|
|
|
|
my $eventmaster = $EVT_FULLLIST;
|
|
# my $eventmaster = $EVT_QUEUE_SOON;
|
|
|
|
my @event_methods =
|
|
(
|
|
"dummy",
|
|
"Full List",
|
|
"Soon Queue",
|
|
);
|
|
|
|
#######################################################################
|
|
#
|
|
# Soon Queue data.
|
|
#
|
|
|
|
my $QUEUE_ERRTIME = 60; # Time to sleep on rollrec error.
|
|
|
|
#
|
|
# $QUEUE_SOONLIMIT defines the length of "soon". When building the soon
|
|
# queue, any zone with an event between now and (now + $QUEUE_SOONLIMIT)
|
|
# will be added to the soon queue. This is a seconds count.
|
|
#
|
|
# This value will depend on the number of managed zones and their lifespans.
|
|
# The default value is for a day, which means the soon queue will hold all
|
|
# events that will occur within the next 24 hours.
|
|
#
|
|
my $QUEUE_SOONLIMIT = 86400;
|
|
|
|
my $QUEUE_RUNSCAN = '<<< run full scan >>>'; # Fake rollrec name to trigger
|
|
# a full scan.
|
|
|
|
my %queue_eventtimes = ();
|
|
my %queue_maxttls = ();
|
|
my %queue_signtimes = ();
|
|
my @queue_allzones = ();
|
|
my @queue_sooners = ();
|
|
|
|
my $queue_firstsoon; # Index of first unprocessed soon entry.
|
|
my $queue_lastscan; # GMT of last full scan.
|
|
my $queue_scantime; # Time taken for last full scan.
|
|
my $queue_scanskips; # Count of zones skipped in scan.
|
|
my $queue_soonend; # End of current "soon" period.
|
|
|
|
#######################################################################
|
|
|
|
my $queued_int = 0; # Queued-SIGINT flag.
|
|
my $queued_hup = 0; # Queued-SIGHUP flag.
|
|
|
|
my $wassigned; # Flag indicating zone was signed.
|
|
|
|
my $ret; # Return code from main().
|
|
my $runerr; # Execution error -- used in runner().
|
|
|
|
#
|
|
# If we find the rollrec file is empty, we'll give an error message
|
|
# only on an occasional pass through the zone list.
|
|
#
|
|
my $MAXRRFERRS = 5; # Number of list passes to stay quiet.
|
|
my $rrferrors = 0; # Count of times through list.
|
|
|
|
#
|
|
# Detect required Perl modules.
|
|
#
|
|
dnssec_tools_load_mods('Date::Parse' => "",
|
|
'Date::Format' => "",);
|
|
|
|
#
|
|
# Do Everything.
|
|
#
|
|
$ret = main();
|
|
exit($ret);
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: main()
|
|
#
|
|
# Purpose: Do Everything.
|
|
#
|
|
# basic steps:
|
|
# while rollrec file is not empty
|
|
# read rollrec file
|
|
#
|
|
# for each rollrec in the rollrec file
|
|
# handle according to its phase
|
|
#
|
|
sub main
|
|
{
|
|
my $olderr; # Saved error action.
|
|
my $ret; # General return code.
|
|
|
|
#
|
|
# Initialize a few things...
|
|
# - force unbuffered output
|
|
# - default error action
|
|
# - set run-packed flag
|
|
#
|
|
$| = 1;
|
|
erraction(ERR_MSG);
|
|
$packed = runpacked();
|
|
|
|
#
|
|
# Parse our command line into an options hash.
|
|
#
|
|
GetOptions(\%opts,@opts) || usage();
|
|
|
|
#
|
|
# Set up the appropriate DNSSEC-Tools config file. If we're running
|
|
# in a packed configuration, we'll use the config file in the packed
|
|
# environment's directory.
|
|
#
|
|
if($packed)
|
|
{
|
|
$opts{$OPT_DIR} = ".";
|
|
setconffile("$ENV{'PAR_TEMP'}/inc/dnssec-tools.conf");
|
|
}
|
|
|
|
#
|
|
# If there's a -dtconfig command line option, we'll use that,
|
|
#
|
|
if(exists($opts{$OPT_DTCONF}))
|
|
{
|
|
setconffile($opts{$OPT_DTCONF});
|
|
}
|
|
|
|
#
|
|
# Check our options and arguments.
|
|
#
|
|
optsandargs();
|
|
|
|
#
|
|
# Run our configuration checks and make sure any error messages
|
|
# will be printed.
|
|
#
|
|
$olderr = erraction(ERR_MSG);
|
|
# if(dt_confcheck(0) > 0)
|
|
# {
|
|
# dt_confcheck(1);
|
|
# print STDERR "\nrollerd: configuration checks failed, halting\n";
|
|
# rolllog_log(LOG_ALWAYS,"","rollerd configuration checks failed");
|
|
# cleanup();
|
|
# }
|
|
erraction($olderr);
|
|
|
|
#
|
|
# Check our required external commands.
|
|
#
|
|
getprogs();
|
|
cmdcheck(\%opts);
|
|
|
|
#
|
|
# Daemonize ourself.
|
|
#
|
|
exit(0) if(!$singlerun && !$foreground && fork());
|
|
POSIX::setsid();
|
|
|
|
#
|
|
# Ensure we're the only rollerd running and drop a pid file.
|
|
#
|
|
if(rollmgr_dropid() == 0)
|
|
{
|
|
print STDERR "another rollerd is already running\n";
|
|
rolllog_log(LOG_ALWAYS,"","another rollerd tried to start");
|
|
cleanup();
|
|
}
|
|
|
|
#
|
|
# If it hasn't been set yet, get the pathname for zonesigner.
|
|
#
|
|
if(($zonesigner eq '') &&
|
|
(($zonesigner=dt_cmdpath('zonesigner')) eq ''))
|
|
{
|
|
print STDERR "no absolute path defined for zonesigner; exiting...\n";
|
|
rolllog_log(LOG_ALWAYS,"","no absolute path defined for zonesigner; exiting...");
|
|
cleanup();
|
|
}
|
|
|
|
#
|
|
# Tell the log we're up.
|
|
#
|
|
bootmsg(1);
|
|
|
|
#
|
|
# Mark the domains as being under our control.
|
|
#
|
|
eminent_domains($rollrecfile);
|
|
|
|
#
|
|
# Set up the command channel.
|
|
#
|
|
if(($ret=rollmgr_channel(1)) != 1)
|
|
{
|
|
my @errs =
|
|
(
|
|
'Unable to connect to the server.',
|
|
'Unable to create a Unix socket.',
|
|
'Unable to bind to the Unix socket.',
|
|
'Unable to change the permissions on the Unix socket.',
|
|
'Unable to listen on the Unix socket.',
|
|
'Communications socket name was longer than allowed for a Unix socket.',
|
|
);
|
|
|
|
#
|
|
# Adjust for array index.
|
|
#
|
|
$ret *= -1;
|
|
|
|
rolllog_log(LOG_FATAL,"","unable to create control communications channel: $errs[$ret]");
|
|
|
|
exit(3);
|
|
}
|
|
|
|
#
|
|
# Main event loop. If the rollrec file is okay, we'll read it,
|
|
# check its zones -- rolling 'em if need be -- and saving its state.
|
|
# We'll always check for user commands and then sleep a bit.
|
|
#
|
|
if($eventmaster == $EVT_FULLLIST)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,""," ");
|
|
|
|
full_list_event_loop();
|
|
}
|
|
if($eventmaster == $EVT_QUEUE_SOON)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,""," ");
|
|
queue_soon_event_loop();
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_FATAL,"","invalid event handler specified; cannot continue");
|
|
print STDERR "rollerd: invalid event handler specified; cannot continue\n";
|
|
exit(1);
|
|
}
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: eminent_domains()
|
|
#
|
|
# Purpose: Mark the domains in the rollrec file as being under our
|
|
# control.
|
|
#
|
|
sub eminent_domains
|
|
{
|
|
my $rrf = shift; # Rollrec file.
|
|
|
|
#
|
|
# Exit with failure if the rollrec file is bad.
|
|
#
|
|
if(!rrfokay($rrf,""))
|
|
{
|
|
rolllog_log(LOG_FATAL,'',"rollrec file \"$rrf\" invalid");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Get the current contents of the rollrec file.
|
|
#
|
|
rollrec_lock();
|
|
rollrec_read($rollrecfile);
|
|
|
|
#
|
|
# For each rollrec entry, get the keyrec file and mark its zone
|
|
# entry as being controlled by us.
|
|
#
|
|
foreach my $rname (rollrec_names())
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
my $prefix; # Rollrec's directory field.
|
|
my $keyrec; # Keyrec's filename.
|
|
|
|
#
|
|
# Get the rollrec for this name. If it doesn't have one,
|
|
# whinge and continue to the next.
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"no rollrec defined for zone");
|
|
next;
|
|
}
|
|
|
|
#
|
|
# Check for a directory. We'll use rollerd's execution
|
|
# directory if one isn't defined.
|
|
#
|
|
$prefix = $xqtdir;
|
|
$prefix = $rrr->{'directory'} if(defined($rrr->{'directory'}));
|
|
|
|
#
|
|
# Build the keyrec file.
|
|
#
|
|
$keyrec = $rrr->{'keyrec'};
|
|
$keyrec = "$prefix/$keyrec" if($keyrec !~ /^\//);
|
|
|
|
#
|
|
# Set the error flag if either the zonefile or the keyrec
|
|
# file don't exist.
|
|
#
|
|
if(! -e $keyrec)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec \"$keyrec\" does not exist");
|
|
next;
|
|
}
|
|
|
|
#
|
|
# Mark the keyrec's zone as being under our control.
|
|
#
|
|
keyrec_read($keyrec);
|
|
keyrec_setval('zone',$rrr->{'zonename'},'rollmgr',"rollerd");
|
|
keyrec_close();
|
|
}
|
|
|
|
#
|
|
# Save the current rollrec file state.
|
|
#
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Return success.
|
|
#
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: full_list_event_loop()
|
|
#
|
|
# Purpose: Rollover event handler -- full queue.
|
|
#
|
|
# Every $sleeptime seconds, it checks the entire set of rollrecs
|
|
# to see if any rollover actions must be taken.
|
|
#
|
|
# This method works fine for small numbers of zones; it gets
|
|
# unwieldy as the number of managed zones increases.
|
|
#
|
|
# This is the original event-loop code.
|
|
#
|
|
sub full_list_event_loop
|
|
{
|
|
while(42)
|
|
{
|
|
# rolllog_log(LOG_ALWAYS,"","looping ----------------------------------------");
|
|
|
|
#
|
|
# Turn off signal handlers so they don't interrupt us
|
|
# while we're running the queue.
|
|
#
|
|
controllers(0);
|
|
$sleep_override = 0;
|
|
|
|
#
|
|
# Return to our execution directory.
|
|
#
|
|
rolllog_log(LOG_TMI,'',"execution directory: chdir($xqtdir)");
|
|
chdir($xqtdir);
|
|
|
|
#
|
|
# If we have a valid rollrec file, we'll read its contents
|
|
# and handle for expired KSKs and ZSKs.
|
|
#
|
|
if(rrfchk($rollrecfile))
|
|
{
|
|
my $kronos1; # Check's start time.
|
|
my $kronos2; # Check's end time.
|
|
my $kronodiff; # Difference of times.
|
|
my $kronos; # difference string.
|
|
|
|
#
|
|
# Get the contents of the rollrec file and check
|
|
# for expired KSKs and ZSKs.
|
|
#
|
|
rollrec_lock();
|
|
if(rollrec_read($rollrecfile) > 0)
|
|
{
|
|
#
|
|
# Check the zones for expired ZSKs. We'll also
|
|
# keep track of how long it takes to check the
|
|
# ZSKs.
|
|
#
|
|
$kronos1 = time;
|
|
rollkeys();
|
|
$kronos2 = time;
|
|
$kronodiff = $kronos2 - $kronos1;
|
|
$kronos = timetrans($kronodiff);
|
|
rolllog_log(LOG_TMI,"<timer>","keys checked in $kronos");
|
|
|
|
#
|
|
# Save the current rollrec file state.
|
|
#
|
|
rollrec_close();
|
|
}
|
|
rollrec_unlock();
|
|
}
|
|
|
|
#
|
|
# Check for user commands.
|
|
#
|
|
commander();
|
|
|
|
#
|
|
# We'll stop now if we're only running the queue once.
|
|
#
|
|
if($singlerun)
|
|
{
|
|
rolllog_log(LOG_INFO,"","rollover manager shutting down at end of single-run execution");
|
|
halt_handler();
|
|
exit(0);
|
|
}
|
|
|
|
#
|
|
# Turn on our signal handlers and then take a nap.
|
|
#
|
|
controllers(1);
|
|
sleeper($sleeptime);
|
|
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: rollkeys()
|
|
#
|
|
# Purpose: Go through the zones in the rollrec file and start rolling
|
|
# the ZSKs and KSKs for those which have expired.
|
|
#
|
|
sub rollkeys
|
|
{
|
|
my @rrfstats; # Stats for rollrec file.
|
|
|
|
#
|
|
# Let the display program know we're starting a roll cycle.
|
|
#
|
|
display("startroll \"<all>\" 0");
|
|
|
|
#
|
|
# Check the zones in the rollrec file to see if they're ready
|
|
# to roll.
|
|
#
|
|
foreach my $rname (rollrec_names())
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
#
|
|
# Close down if we've received an INT signal.
|
|
#
|
|
if($queued_int)
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"received immediate shutdown command");
|
|
halt_handler();
|
|
}
|
|
|
|
#
|
|
# Return to our execution directory.
|
|
#
|
|
rolllog_log(LOG_TMI,$rname,"execution directory: chdir($xqtdir)");
|
|
chdir($xqtdir);
|
|
|
|
#
|
|
# Ensure the logging level is set correctly.
|
|
#
|
|
$loglevel = $loglevel_save;
|
|
|
|
#
|
|
# Get the rollrec for this name. If it doesn't have one,
|
|
# whinge and continue to the next.
|
|
# (This should never happen, but...)
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"no rollrec defined for zone");
|
|
next;
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Set the logging level to the rollrec entry's level (if it
|
|
# has one) for the duration of processing this zone.
|
|
#
|
|
$loglevel_save = $loglevel;
|
|
if(defined($rr{'loglevel'}))
|
|
{
|
|
my $llev; # Logging level.
|
|
|
|
$llev = rolllog_num($rr{'loglevel'});
|
|
if($llev != -1)
|
|
{
|
|
$loglevel = $llev;
|
|
rolllog_level($loglevel,0);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"invalid rollrec logging level \"$rr{'loglevel'}\"");
|
|
}
|
|
}
|
|
|
|
#
|
|
# Don't do anything with skip records.
|
|
#
|
|
if($rr{'rollrec_type'} eq "skip")
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"is a skip rollrec");
|
|
next;
|
|
}
|
|
|
|
#
|
|
# If this rollrec has a directory record, we'll move into that
|
|
# directory for execution; if it doesn't we'll stay put.
|
|
# If the chdir() fails, we'll skip this rollrec.
|
|
#
|
|
next if(chrrdir($rname,$rr{'directory'},'rollkeys:') == 0);
|
|
|
|
#
|
|
# If the zone's keyrec file doesn't exist, we'll try to
|
|
# create it with a simple zonesigner call.
|
|
#
|
|
if(! -e $rrr->{'keyrec'})
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec \"$rrr->{'keyrec'}\" does not exist; running initial zonesigner");
|
|
signer($rname, "initial", 0);
|
|
}
|
|
|
|
#
|
|
# Ensure the record has the KSK and ZSK phase fields.
|
|
#
|
|
if($rr{'kskphase'} == -1)
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"new kskphase entry");
|
|
nextphase($rname,$rrr,0,'KSK')
|
|
}
|
|
if($rr{'zskphase'} == -1)
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"new zskphase entry");
|
|
nextphase($rname,$rrr,0,'ZSK')
|
|
}
|
|
|
|
#
|
|
# Turn off the flag indicating that the zone was signed.
|
|
#
|
|
$wassigned = 0;
|
|
|
|
#
|
|
# If this zone's current KSK has expired, we'll get it rolling.
|
|
#
|
|
if(ksk_expired($rname,$rrr,"kskcur"))
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"current KSK has expired\n") if($rr{'zskphase'} == 0);
|
|
|
|
ksk_phaser($rname,$rrr);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"current KSK still valid");
|
|
}
|
|
|
|
#
|
|
# If this zone's current ZSK has expired, we'll get it rolling.
|
|
#
|
|
if(zsk_expired($rname,$rrr,"zskcur"))
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"current ZSK has expired\n") if($rr{'zskphase'} == 0);
|
|
zsk_phaser($rname,$rrr);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"current ZSK still valid");
|
|
}
|
|
|
|
#
|
|
# If -alwayssign was specified, always sign the zone
|
|
# even if we didn't need to for this period.
|
|
#
|
|
if($alwayssign && !$wassigned)
|
|
{
|
|
my $krr; # Ref to keyrec data.
|
|
my $extraargs = ""; # Phase-dependent argument.
|
|
|
|
rolllog_log(LOG_TMI,$rname, "signing the zone $rrr->{'zonename'} (-alwayssign specified)");
|
|
|
|
#
|
|
# Check value and file existence, etc.
|
|
#
|
|
$krr = opts_zonekr($rrr->{'keyrec'},$rrr->{'zonename'});
|
|
|
|
#
|
|
# Tell the signer what phase we're in so it
|
|
# can decide what key to use.
|
|
#
|
|
if($rrr->{'zskphase'} > 0)
|
|
{
|
|
$extraargs = "ZSK phase $rrr->{'zskphase'}";
|
|
}
|
|
elsif($rrr->{'kskphase'} > 0)
|
|
{
|
|
$extraargs = "KSK phase $rrr->{'kskphase'}";
|
|
}
|
|
|
|
#
|
|
# KSK signing uses double-signature so nothing
|
|
# is needed since zonesigner always uses all
|
|
# available keys.
|
|
#
|
|
|
|
#
|
|
# Actually do the signing.
|
|
#
|
|
my $ret = signer($rname, $extraargs, $krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR, "signing $rname failed!");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#
|
|
# Ensure the logging level is set correctly.
|
|
#
|
|
$loglevel = $loglevel_save;
|
|
rolllog_level($loglevel,0);
|
|
}
|
|
|
|
#=============================================================================
|
|
#=============================================================================
|
|
#=============================================================================
|
|
#=============================================================================
|
|
#=============================================================================
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_soon_event_loop()
|
|
#
|
|
# Purpose: Rollover event handler -- timed event queue with "soon" queue.
|
|
#
|
|
# Periodically, the entire set of rollrecs is scanned to find
|
|
# the set of events that will occur "soon". ("soon" may be
|
|
# defined as within the next 12 hours, 24 hours, etc.)
|
|
# These rollrecs are added to a queue in chronological order.
|
|
#
|
|
# rollerd will sleep until the next event occurs, then handle
|
|
# those events that have triggered. The next event-expiration
|
|
# times are recalculated for the list of "soon" events as well
|
|
# as the events which have just been handled. If the just-
|
|
# handled events fall within the "soon" timeframe, they are
|
|
# reinserted in the "soon" queue.
|
|
#
|
|
# Rescanning of the full set of rollrecs must occur periodically
|
|
# in order to keep the "soon" queue populated.
|
|
#
|
|
# The definition of "soon" and the rescan time period are not
|
|
# universally definite numbers. They will almost certainly
|
|
# depend on the numbers of zones managed by each installation,
|
|
# plus the key lifespans for those zones.
|
|
#
|
|
# This event loop was added in Spring, 2011.
|
|
#
|
|
sub queue_soon_event_loop
|
|
{
|
|
rolllog_log(LOG_ALWAYS,""," ");
|
|
rolllog_log(LOG_ALWAYS,""," not worrying about signal handlers at the moment!");
|
|
rolllog_log(LOG_ALWAYS,""," what should happen with -singlerun?");
|
|
rolllog_log(LOG_ALWAYS,""," ");
|
|
rolllog_log(LOG_INFO,""," QUEUE_SOONLIMIT - $QUEUE_SOONLIMIT");
|
|
rolllog_log(LOG_ALWAYS,""," ");
|
|
|
|
#
|
|
# Time for the Main Event*!
|
|
#
|
|
# * loop
|
|
#
|
|
while(42)
|
|
{
|
|
# rolllog_log(LOG_ALWAYS,"","looping full -----------------------------------");
|
|
|
|
#
|
|
# Turn off signal handlers while we're reading the rollrec file.
|
|
#
|
|
controllers(0);
|
|
|
|
#
|
|
# Return to our execution directory.
|
|
#
|
|
rolllog_log(LOG_TMI,'',"execution directory: chdir($xqtdir)");
|
|
chdir($xqtdir);
|
|
|
|
#
|
|
# If we have a valid rollrec file, we'll read its contents
|
|
# and handle for expired KSKs and ZSKs.
|
|
#
|
|
if(rrfchk($rollrecfile) == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,"","rrfchk($rollrecfile) failed");
|
|
sleeper($QUEUE_ERRTIME);
|
|
next;
|
|
}
|
|
|
|
#
|
|
# Read the rollrec file.
|
|
#
|
|
# WARNING: We're only locking the file while we're reading
|
|
# it! This may not be the safest way to do this.
|
|
#
|
|
rollrec_lock();
|
|
rollrec_read($rollrecfile);
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Turn on signal handlers while we're scanning the full queue.
|
|
#
|
|
controllers(1);
|
|
|
|
#
|
|
# Calculate event times for each rollrec entry.
|
|
#
|
|
queue_scanall();
|
|
|
|
# queue_prteventtimes();
|
|
|
|
#
|
|
# Check all the events that will be happening "soon".
|
|
#
|
|
while(42)
|
|
{
|
|
my $nextrrn; # Next rollrec to handle.
|
|
my $naptime; # Time until next event.
|
|
my $now; # Current time.
|
|
my $ind; # Loop index.
|
|
my @savenames = (); # Rollrecs to return to soonlist
|
|
my $kronos1; # Start time for soon loop.
|
|
my $kronos2; # End time for soon loop.
|
|
my $kronos; # Time taken in soon loop.
|
|
|
|
# rolllog_log(LOG_ALWAYS,"","looping soon -----------------------");
|
|
#
|
|
# Rescan the entire list of zones if the soon list
|
|
# is empty.
|
|
#
|
|
# This should never happen. There should always be,
|
|
# at the very least, an entry that schedules the next
|
|
# full scan.
|
|
#
|
|
if(@queue_sooners == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,"","DANGER!!! soonlist is empty and shouldn't be!");
|
|
goto endsooners;
|
|
}
|
|
|
|
# queue_prteventtimes();
|
|
|
|
#
|
|
# Get the time until the next event.
|
|
#
|
|
$nextrrn = $queue_sooners[0];
|
|
$naptime = $queue_eventtimes{$nextrrn};
|
|
|
|
#
|
|
# Turn on our signal handlers and then take a nap.
|
|
#
|
|
controllers(1);
|
|
|
|
# $naptime = 60 if($naptime <= 0);
|
|
$sleeptime = $naptime;
|
|
# queue_prtsooners();
|
|
# rolllog_log(LOG_ALWAYS,"","queue_sooners (" . @queue_sooners . " entries)");
|
|
sleeper($naptime) if($naptime > 0);
|
|
|
|
#
|
|
# Turn off signal handlers so they don't interrupt us
|
|
# while we're scanning the soon queue.
|
|
#
|
|
controllers(0);
|
|
$sleep_override = 0;
|
|
|
|
#
|
|
# Get this iteration's idea of the current time.
|
|
#
|
|
$now = gmtime(time);
|
|
$now = str2time($now);
|
|
|
|
#
|
|
# Calculate the next-event time for each of the zones
|
|
# in the @queue_sooners list.
|
|
#
|
|
rolllog_log(LOG_TMI,"","starting scan of soon queue");
|
|
$kronos1 = time;
|
|
queue_scansooners($now);
|
|
$kronos2 = time;
|
|
$kronos = $kronos2 - $kronos1;
|
|
$kronos = timetrans($kronos);
|
|
# rolllog_log(LOG_ALWAYS,"","scanned soon queue (" . @queue_sooners . " zones) in $kronos");
|
|
|
|
#
|
|
# Get the rollrec file.
|
|
#
|
|
rollrec_lock();
|
|
if(rollrec_read($rollrecfile) <= 0)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"","DANGER!!! rollrec_read($rollrecfile) failed");
|
|
last;
|
|
}
|
|
|
|
#
|
|
# Reset the index for the next imminent soon event.
|
|
#
|
|
$queue_firstsoon = 0;
|
|
|
|
#
|
|
# Turn on signal handlers so the user can query the
|
|
# queue while we're running the queue.
|
|
#
|
|
controllers(1);
|
|
|
|
#
|
|
# Run through our queue of soon-to-happen rollover
|
|
# events.
|
|
#
|
|
$kronos1 = time;
|
|
for($ind=0; $ind < @queue_sooners; $ind++)
|
|
{
|
|
my $rrn = $queue_sooners[$ind];
|
|
my $rrr;
|
|
my $nxt;
|
|
|
|
#
|
|
# Check for user commands.
|
|
#
|
|
commander();
|
|
|
|
#
|
|
# Rescan the entire list of zones if we've hit
|
|
# the run-scan fake rollrec name.
|
|
#
|
|
#
|
|
# goto endsooners if($rrn eq $QUEUE_RUNSCAN);
|
|
if($rrn eq $QUEUE_RUNSCAN)
|
|
{
|
|
rolllog_log(LOG_TMI,"","RUNSCAN hit! stopping soon-loop");
|
|
goto endsooners;
|
|
}
|
|
|
|
$rrr = rollrec_fullrec($rrn);
|
|
|
|
#
|
|
# Skip this zone if this is a skip zone.
|
|
#
|
|
next if($rrr->{'rollrec_type'} eq 'skip');
|
|
|
|
#
|
|
# Proceed to the next zone if the zone is in a
|
|
# state that requires administrative attention.
|
|
#
|
|
next if($rrr->{'kskphase'} == 6);
|
|
|
|
if($queue_eventtimes{$rrn} > 0)
|
|
{
|
|
rollrec_unlock();
|
|
last;
|
|
}
|
|
|
|
#
|
|
# Handle the rollover event.
|
|
#
|
|
queue_rollevent($rrn);
|
|
$queue_firstsoon++;
|
|
|
|
#
|
|
# Add this zone to our savelist if it has its
|
|
# next rollover event soon.
|
|
#
|
|
$nxt = queue_scanone($rrn,$now);
|
|
if(($nxt < $queue_eventtimes{$QUEUE_RUNSCAN}) &&
|
|
(! $singlerun))
|
|
{
|
|
push @savenames, $rrn;
|
|
}
|
|
|
|
}
|
|
$kronos2 = time;
|
|
|
|
#
|
|
# Save the current rollrec file state and get the
|
|
# time stats.
|
|
#
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
$kronos = $kronos2 - $kronos1;
|
|
$kronos = timetrans($kronos);
|
|
|
|
#
|
|
# Check for user commands.
|
|
#
|
|
commander();
|
|
|
|
#
|
|
# We'll stop now if we're only running the queue once.
|
|
#
|
|
if($singlerun)
|
|
{
|
|
rolllog_log(LOG_INFO,"","rollover manager shutting down at end of single-run execution");
|
|
halt_handler();
|
|
exit(0);
|
|
}
|
|
|
|
#
|
|
# Remove the zones we've just handled from the sooners
|
|
# list. Also, add in the zones that we just handled
|
|
# that are still within the "soon" time.
|
|
#
|
|
splice @queue_sooners, 0, $ind;
|
|
$queue_firstsoon = 0;
|
|
push @queue_sooners, @savenames;
|
|
|
|
#
|
|
# Sort the sooners list. The sorting is done based on
|
|
# the amount of time before each zone's next event.
|
|
#
|
|
@queue_sooners = sort { $queue_eventtimes{$a} <=> $queue_eventtimes {$b} } @queue_sooners;
|
|
|
|
}
|
|
|
|
endsooners:
|
|
rollrec_unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_scanall()
|
|
#
|
|
# Purpose: Calculate the next-event time for each of the zones in the
|
|
# rollrec file. Zones that fall within our "soon" limit, will
|
|
# be added to the @queue_sooners list.
|
|
#
|
|
sub queue_scanall
|
|
{
|
|
my $cronus; # Current time.
|
|
my $kronos1; # Scan start time.
|
|
my $kronos2; # Scan end time.
|
|
|
|
my $lasttime; # Last rollrec's event time.
|
|
|
|
# rolllog_log(LOG_ALWAYS,"","queue_scanall: running a full scan");
|
|
|
|
#
|
|
# Reset all our queuing lists.
|
|
#
|
|
%queue_eventtimes = ();
|
|
@queue_allzones = ();
|
|
@queue_sooners = ();
|
|
$queue_scanskips = 0;
|
|
|
|
#
|
|
# Get the base time.
|
|
#
|
|
$kronos1 = time;
|
|
$cronus = gmtime($kronos1);
|
|
$cronus = str2time($cronus);
|
|
|
|
#
|
|
# Recalculate the next-event time for every zone in the rollrec file.
|
|
# If the zone falls within the soon limit, it'll be added to
|
|
# @queue_sooners. All zones will be added to @queue_allzones.
|
|
#
|
|
foreach my $rrn (rollrec_names())
|
|
{
|
|
my $next;
|
|
|
|
$next = queue_scanone($rrn,$cronus);
|
|
|
|
push @queue_allzones, $rrn;
|
|
push @queue_sooners, $rrn if($next < $QUEUE_SOONLIMIT);
|
|
}
|
|
|
|
#
|
|
# Sort the zone lists we'll use for queuing. The sorting is done
|
|
# based on the amount of time before each zone's next event.
|
|
#
|
|
@queue_allzones = sort { $queue_eventtimes{$a} <=> $queue_eventtimes {$b} } @queue_allzones;
|
|
@queue_sooners = sort { $queue_eventtimes{$a} <=> $queue_eventtimes {$b} } @queue_sooners;
|
|
|
|
#
|
|
# Figure out how long the scan took.
|
|
#
|
|
$queue_lastscan = time;
|
|
$kronos2 = time;
|
|
$queue_scantime = $kronos2 - $kronos1;
|
|
$queue_soonend = $cronus + $QUEUE_SOONLIMIT;
|
|
|
|
#
|
|
# Arrange for the next full scan.
|
|
#
|
|
$queue_eventtimes{$QUEUE_RUNSCAN} = $QUEUE_SOONLIMIT;
|
|
push @queue_sooners, $QUEUE_RUNSCAN;
|
|
|
|
#
|
|
# If no zones fall within the "soon" timeframe, we'll schedule
|
|
# another scan for shortly before the next zone needs to be handled.
|
|
#
|
|
if(@queue_sooners == 0)
|
|
{
|
|
my $rrn; # Rollrec name.
|
|
my $nevt; # Next event time.
|
|
|
|
$rrn = @queue_allzones[0];
|
|
$nevt = $queue_eventtimes{$rrn};
|
|
|
|
$sleeptime = $nevt - $queue_scantime - 60;
|
|
}
|
|
|
|
# queue_prteventtimes();
|
|
# queue_prtsooners();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_scansooners()
|
|
#
|
|
# Purpose: Calculate the next-event time for each of the zones in the
|
|
# @queue_sooners list. If the zone still falls within our
|
|
# "soon" limit, it'll be kept in the @queue_sooners list.
|
|
#
|
|
sub queue_scansooners
|
|
{
|
|
my $cronus = shift; # Current time.
|
|
my @newsooners = (); # Temporary list of new sooners.
|
|
my $nextscan; # Time of next full scan.
|
|
my $tdiff; # Time difference between now and last scan.
|
|
|
|
# rolllog_log(LOG_INFO,"","queue_scansooners: down in");
|
|
|
|
#
|
|
# Figure out when the next full scan will take place.
|
|
#
|
|
$tdiff = time - $queue_lastscan;
|
|
$nextscan = $QUEUE_SOONLIMIT - $tdiff;
|
|
$queue_eventtimes{$QUEUE_RUNSCAN} = $nextscan;
|
|
|
|
#
|
|
# Recalculate the next-event time for each zone in the @queue_sooners
|
|
# list. If the zone is still within the soon limit, it'll be kept in
|
|
# @queue_sooners.
|
|
#
|
|
foreach my $rrn (@queue_sooners)
|
|
{
|
|
my $nxt;
|
|
|
|
#
|
|
# Close down if we've received an INT signal.
|
|
#
|
|
if($queued_int)
|
|
{
|
|
rolllog_log(LOG_INFO,$rrn,"received immediate shutdown command");
|
|
halt_handler();
|
|
}
|
|
|
|
next if($rrn eq $QUEUE_RUNSCAN);
|
|
|
|
$nxt = queue_scanone($rrn,$cronus);
|
|
push @newsooners, $rrn if($nxt < $nextscan);
|
|
}
|
|
|
|
#
|
|
# Schedule the next full scan.
|
|
#
|
|
push @newsooners, $QUEUE_RUNSCAN;
|
|
|
|
#
|
|
# Sort the new list of soon zones. The sorting is done based
|
|
# on the amount of time before each zone's next event.
|
|
#
|
|
@newsooners = sort { $queue_eventtimes{$a} <=> $queue_eventtimes {$b} } @newsooners;
|
|
|
|
#
|
|
# Replace our soon list.
|
|
#
|
|
@queue_sooners = @newsooners;
|
|
|
|
# queue_prtsooners();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_scanone()
|
|
#
|
|
# Purpose: Find the number of seconds until the given zone's next event.
|
|
# The event time will be saved in the Big List o' Event Times.
|
|
#
|
|
sub queue_scanone
|
|
{
|
|
my $rrn = shift; # Rollrec name.
|
|
my $cronus = shift; # Time to check.
|
|
|
|
my $next; # Rollrec's next event time.
|
|
|
|
#
|
|
# Get the zone's next-event time.
|
|
#
|
|
$next = queue_evttime($rrn,$cronus);
|
|
|
|
#
|
|
# Set the zone's time data and return the value to our caller.
|
|
#
|
|
$queue_eventtimes{$rrn} = $next;
|
|
return($next);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_evttime()
|
|
#
|
|
# Purpose: Find the number of seconds until the given zone's next event.
|
|
# An event is either when a zone moves into key rollover or when
|
|
# a zone already in rollover moves to the next rollover phase.
|
|
#
|
|
# If a zone is not in rollover, the event time is calculated by
|
|
|
|
# We'll calculate this time based on the maximum TTL in the
|
|
# zone's signed zonefile. However, if If the signed zonefile
|
|
# hasn't been modified since we last checked, we'll use the
|
|
# maximum TTL we saved the last time we noticed it had been
|
|
# updated.
|
|
#
|
|
sub queue_evttime
|
|
{
|
|
my $rrn = shift; # Name of rollrec to check.
|
|
my $cronus = shift; # Time to calculate from.
|
|
|
|
my %rr; # Rollrec hash.
|
|
my $rrr; # Reference to rollrec.
|
|
|
|
my $zonefile; # Zone file.
|
|
my @szstat; # stat() info for signed zone.
|
|
my $maxttl; # Zone's maximum TTL value.
|
|
my $phstart; # Start of zone's phase.
|
|
|
|
my $endtime; # End-date in seconds.
|
|
my $timediff; # Time until expiration.
|
|
|
|
#
|
|
# Get the zone's rollrec.
|
|
#
|
|
$rrr = rollrec_fullrec($rrn);
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Get a few fields from the rollrec.
|
|
#
|
|
$zonefile = $rrr->{'zonefile'};
|
|
$phstart = $rrr->{'phasestart'};
|
|
|
|
#
|
|
# If we're not in rollover, the event time is calculated based
|
|
# on key lifetime.
|
|
# If we are in rollover, the event time is calculated based on
|
|
# the maximum TTL of the zone's resource records.
|
|
#
|
|
if(($rr{'kskphase'} == 0) && ($rr{'zskphase'} == 0))
|
|
{
|
|
my $krf; # Zone's keyrec filename.
|
|
my $krr; # Zone's keyrec reference.
|
|
my @keys; # Zone's key lists.
|
|
my $kwait; # Time until KSK rollover.
|
|
my $zwait; # Time until ZSK rollover.
|
|
my $kmin = -1; # Minimum KSK lifetime.
|
|
my $zmin = -1; # Minimum ZSK lifetime.
|
|
|
|
#
|
|
# Get the zone's keyrec.
|
|
#
|
|
$krf = $rrr->{'keyrec'};
|
|
keyrec_read($krf);
|
|
keyrec_close();
|
|
|
|
$krr = keyrec_fullrec($rrr->{'zonename'});
|
|
|
|
#
|
|
# Find the minimum key lifespan for the keys in the zone's
|
|
# Current KSK set.
|
|
#
|
|
@keys = split / /, keyrec_signset_keys($rrr->{'zonename'}, 'kskcur');
|
|
foreach my $key (@keys)
|
|
{
|
|
my $life = keyrec_recval($key,'ksklife');
|
|
|
|
$kmin = $life if($kmin == -1);
|
|
$kmin = $life if($life < $kmin);
|
|
}
|
|
|
|
#
|
|
# Find the minimum key lifespan for the keys in the zone's
|
|
# Current ZSK set.
|
|
#
|
|
@keys = split / /, keyrec_signset_keys($rrr->{'zonename'}, 'zskcur');
|
|
foreach my $key (@keys)
|
|
{
|
|
my $life = keyrec_recval($key,'zsklife');
|
|
|
|
$zmin = $life if($zmin == -1);
|
|
$zmin = $life if($life < $zmin);
|
|
}
|
|
|
|
#
|
|
# Figure out how long we have until the next rollover.
|
|
#
|
|
$kwait = ($rrr->{'ksk_rollsecs'} + $kmin) - $cronus;
|
|
$zwait = ($rrr->{'zsk_rollsecs'} + $zmin) - $cronus;
|
|
|
|
#
|
|
# Return the minimum time-until-rollover.
|
|
#
|
|
$timediff = ($kwait < $zwait) ? $kwait : $zwait;
|
|
return($timediff);
|
|
}
|
|
|
|
#
|
|
# If the signed zonefile hasn't been modified since we last
|
|
# checked, we'll use the maximum TTL we saved the last time
|
|
# we noticed it had been updated.
|
|
#
|
|
@szstat = stat($zonefile);
|
|
if($szstat[8] > $queue_signtimes{$rrn})
|
|
{
|
|
#
|
|
# Get the signed zonefile's maximum TTL and double it.
|
|
#
|
|
$maxttl = queue_maxttl($zonefile);
|
|
$maxttl *= 2;
|
|
|
|
#
|
|
# Squirrel away some values we'll use later.
|
|
#
|
|
$queue_maxttls{$rrn} = $maxttl;
|
|
$queue_signtimes{$rrn} = $szstat[8];
|
|
}
|
|
else
|
|
{
|
|
$maxttl = $queue_maxttls{$rrn};
|
|
$queue_scanskips++;
|
|
}
|
|
|
|
#
|
|
# Get the zone's "next event" time by adding the start of its
|
|
# last phase-change to twice the maximum TTL.
|
|
#
|
|
$endtime = str2time($phstart);
|
|
$endtime += $maxttl;
|
|
|
|
#
|
|
# Find the difference between the next-event time and now.
|
|
#
|
|
$timediff = $endtime - $cronus;
|
|
|
|
return($timediff)
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_maxttl()
|
|
#
|
|
# Purpose: Determine the maximum TTL for the resource records in the zone
|
|
# we were given. The max TTL will be returned to the caller.
|
|
#
|
|
sub queue_maxttl
|
|
{
|
|
my $signdb = shift; # Signed zone's db filename.
|
|
|
|
my $rrsref; # Reference to zone's resource records.
|
|
my @rrs; # Zone's resource records.
|
|
my $numrrs; # Number of resource records in zone.
|
|
my $rrref; # Reference to a resource record.
|
|
my %rr; # A resource record to examine.
|
|
my $maxttl = -1; # Zone's maximum time-to-live value.
|
|
my $ttl; # Time-to-live value from a record.
|
|
|
|
#
|
|
# Ensure the zone file exists and is readable.
|
|
#
|
|
if((! -e $signdb) || (! -r $signdb))
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Get the zone's resource record collection.
|
|
#
|
|
$rrsref = dt_parse_zonefile(file => $signdb);
|
|
|
|
#
|
|
# Get the number of resource records in the zone.
|
|
#
|
|
@rrs = @$rrsref;
|
|
$numrrs = @rrs;
|
|
|
|
#
|
|
# Look at each resource record and save the largest value.
|
|
#
|
|
for(my $ind=0;$ind<$numrrs;$ind++)
|
|
{
|
|
$rrref = $rrs[$ind];
|
|
%rr = %$rrref;
|
|
|
|
$ttl = $rr{'ttl'};
|
|
$maxttl = $ttl if($ttl > $maxttl);
|
|
}
|
|
|
|
#
|
|
# Return the zone's maximum TTL value to our caller.
|
|
#
|
|
return($maxttl);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_prteventtimes()
|
|
#
|
|
sub queue_prteventtimes
|
|
{
|
|
foreach my $rrn (@queue_allzones)
|
|
{
|
|
my $rrr;
|
|
my $kpflag;
|
|
my $zpflag;
|
|
|
|
$rrr = rollrec_fullrec($rrn);
|
|
|
|
$kpflag = $rrr->{'kskphase'};
|
|
$zpflag = $rrr->{'zskphase'};
|
|
if($kpflag)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"","\t$rrn\t$queue_eventtimes{$rrn}\tksk expired\n");
|
|
}
|
|
elsif($zpflag)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"","\t$rrn\t$queue_eventtimes{$rrn}\tzsk expired\n");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"","\t$rrn\t$queue_eventtimes{$rrn}\tnormal\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_prtsooners()
|
|
#
|
|
sub queue_prtsooners
|
|
{
|
|
my $maxind; # Last index to check.
|
|
my $rrn; # Zone name.
|
|
|
|
rolllog_log(LOG_ALWAYS,"","queue_sooners (" . @queue_sooners . " entries):\n");
|
|
|
|
$maxind = (@queue_sooners < 30) ? @queue_sooners : 30;
|
|
|
|
for(my $ind=0; $ind < $maxind; $ind++)
|
|
{
|
|
$rrn = $queue_sooners[$ind];
|
|
rolllog_log(LOG_ALWAYS,""," <$rrn> $queue_eventtimes{$rrn} seconds until event\n");
|
|
}
|
|
|
|
$rrn = "$QUEUE_RUNSCAN";
|
|
rolllog_log(LOG_ALWAYS,""," <$rrn> $queue_eventtimes{$rrn} seconds until event\n");
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_rollevent()
|
|
#
|
|
# Purpose Handle the rollover event for a single rollrec entry.
|
|
#
|
|
sub queue_rollevent
|
|
{
|
|
my $rname = shift; # Rollrec to handle.
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
rolllog_log(LOG_TMI,$rname,"queue_rollevent: rolling $rname");
|
|
|
|
#
|
|
# Return to our execution directory.
|
|
#
|
|
# rolllog_log(LOG_TMI,$rname,"execution directory: chdir($xqtdir)");
|
|
chdir($xqtdir);
|
|
|
|
#
|
|
# Ensure the logging level is set correctly.
|
|
#
|
|
$loglevel = $loglevel_save;
|
|
|
|
#
|
|
# Get the rollrec for this name. If it doesn't have one, whinge and
|
|
# continue to the next rollrec.
|
|
# (This should never happen, but...)
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"no rollrec defined for zone");
|
|
return;
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Don't do anything with skip records.
|
|
#
|
|
if($rrr->{'rollrec_type'} eq "skip")
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"is a skip rollrec");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Set the logging level to the rollrec entry's level (if it has one)
|
|
# for the duration of processing this zone.
|
|
#
|
|
$loglevel_save = $loglevel;
|
|
if(defined($rr{'loglevel'}))
|
|
{
|
|
my $llev; # Logging level.
|
|
|
|
$llev = rolllog_num($rr{'loglevel'});
|
|
if($llev != -1)
|
|
{
|
|
$loglevel = $llev;
|
|
rolllog_level($loglevel,0);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"invalid rollrec logging level \"$rr{'loglevel'}\"");
|
|
}
|
|
}
|
|
|
|
#
|
|
# If this rollrec has a directory record, we'll move into that
|
|
# directory for execution; if it doesn't we'll stay put.
|
|
# If the chdir() fails, we'll skip this rollrec.
|
|
#
|
|
return if(chrrdir($rname,$rr{'directory'},'queue_rollevent:') == 0);
|
|
|
|
#
|
|
# Ensure the record has the KSK and ZSK phase fields.
|
|
#
|
|
if($rr{'kskphase'} == -1)
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"new kskphase entry");
|
|
nextphase($rname,$rrr,0,'KSK')
|
|
}
|
|
if($rr{'zskphase'} == -1)
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"new zskphase entry");
|
|
nextphase($rname,$rrr,0,'ZSK')
|
|
}
|
|
|
|
#
|
|
# If this zone's current KSK has expired, we'll get it rolling.
|
|
#
|
|
if(ksk_expired($rname,$rrr,"kskcur"))
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"current KSK has expired\n") if($rr{'zskphase'} == 0);
|
|
|
|
ksk_phaser($rname,$rrr);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"current KSK still valid");
|
|
}
|
|
|
|
#
|
|
# If this zone's current ZSK has expired, we'll get it rolling.
|
|
#
|
|
if(zsk_expired($rname,$rrr,"zskcur"))
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"current ZSK has expired\n") if($rr{'zskphase'} == 0);
|
|
zsk_phaser($rname,$rrr);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"current ZSK still valid");
|
|
}
|
|
|
|
#
|
|
# If -alwayssign was specified, always sign the zone, even if we
|
|
# didn't need to for this period.
|
|
#
|
|
|
|
#
|
|
# XXX: This could double sign if some of the above steps already
|
|
# signed the zone. We should only sign if nothing was done above.
|
|
#
|
|
if($alwayssign)
|
|
{
|
|
my $krr; # Ref to keyrec data.
|
|
my $extraargs = ""; # Phase-dependent argument.
|
|
|
|
#
|
|
# This is only safe to do if we're not waiting for caches
|
|
# to expire.
|
|
#
|
|
if(($rrr->{'zskphase'} != 1) &&
|
|
($rrr->{'zskphase'} != 3) &&
|
|
($rrr->{'kskphase'} != 1) &&
|
|
($rrr->{'kskphase'} != 3))
|
|
{
|
|
rolllog_log(LOG_TMI,$rname, "Signing the zone $rrr->{'zonename'} (-alwayssign specified)");
|
|
|
|
#
|
|
# Check value and file existence, etc.
|
|
#
|
|
$krr = opts_zonekr($rrr->{'keyrec'},$rrr->{'zonename'});
|
|
|
|
#
|
|
# Tell the signer what phase we're in so it
|
|
# can decide what key to use.
|
|
#
|
|
if($rrr->{'zskphase'} > 0)
|
|
{
|
|
$extraargs = "ZSK phase $rrr->{'zskphase'}";
|
|
}
|
|
elsif($rrr->{'kskphase'} > 0)
|
|
{
|
|
$extraargs = "KSK phase $rrr->{'kskphase'}";
|
|
}
|
|
|
|
#
|
|
# KSK signing uses double-signature so nothing is needed
|
|
# since zonesigner always uses all available keys.
|
|
#
|
|
|
|
#
|
|
# Actually do the signing.
|
|
#
|
|
my $ret = signer($rname, $extraargs, $krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR, "signing $rname failed!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_TMI,$rname, "Not signing the zone $rrr->{'zonename'} (-alwayssign was specified but we're awaiting cache timeouts so we can't sign now)");
|
|
}
|
|
}
|
|
|
|
#
|
|
# Reset the logging level is set correctly.
|
|
#
|
|
$loglevel = $loglevel_save;
|
|
rolllog_level($loglevel,0);
|
|
}
|
|
|
|
#=============================================================================
|
|
#=============================================================================
|
|
#=============================================================================
|
|
#=============================================================================
|
|
#=============================================================================
|
|
|
|
|
|
###############################################################################
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zsk_expired()
|
|
#
|
|
# Purpose: This routine returns a boolean indicating if the specified
|
|
# zone has an expired ZSK key of the given type.
|
|
#
|
|
# The zone's keyrec file name is taken from the given rollrec
|
|
# entry. The keyrec file is read and the zone's entry found.
|
|
# The key keyrec of the specified key type (currently, just
|
|
# "zskcur") is pulled from the keyrec file. Each key in the
|
|
# named signing set will be checked.
|
|
#
|
|
# Key expiration is determined by comparing the key keyrec's
|
|
# gensecs field to the current time. The key hasn't expired
|
|
# if the current time is less than the gensecs; the key has
|
|
# expired if the current time is greater than the gensecs.
|
|
#
|
|
sub zsk_expired
|
|
{
|
|
my $rname = shift; # Name of rollrec rec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $keyset = shift; # Key to check.
|
|
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $krname; # Name of keyrec.
|
|
my $krec; # Keyrec reference.
|
|
my %set; # Keyrec hash.
|
|
|
|
my $khr; # Ref to key's hash.
|
|
my %kh; # Key's hash.
|
|
|
|
my @signset; # Key's signing set.
|
|
|
|
my $chronostr; # Text expiration time.
|
|
my $cronus; # Current time.
|
|
my $expired = 0; # Expired-zone flag.
|
|
my $minlife = -1; # Minimum ZSK life.
|
|
my $minhr; # Ref to min-key's keyrec.
|
|
my $rolltime; # Time roll should occur.
|
|
my $starter; # Time 0 for calc'ing rolltime.
|
|
my $waitsecs; # Time to expiration.
|
|
|
|
#
|
|
# Get the rollrec's hash.
|
|
#
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# If this zone is in the middle of KSK rollover, we'll stop
|
|
# working on ZSK rollover.
|
|
#
|
|
if($rr{'kskphase'} > 0)
|
|
{
|
|
my $pstr; # Phase string.
|
|
$pstr = ": " . rollmgr_get_phase('KSK', $rr{'kskphase'});
|
|
$pstr = '' if($pstr eq ": ");
|
|
|
|
rolllog_log(LOG_TMI,$rname,"in KSK rollover (phase $rr{'kskphase'}$pstr); not attempting ZSK rollover");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# If this zone is in the middle of rollover processing, we'll
|
|
# immediately assume the key has expired.
|
|
#
|
|
return(1) if($rr{'zskphase'} > 0);
|
|
|
|
#
|
|
# Get the rollin' key's keyrec for our zone.
|
|
#
|
|
$krname = $rr{'keyrec'};
|
|
$krec = zonekeykr($rname,$rr{'zonename'},$krname,$keyset);
|
|
if($krec == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to find a keyrec for ZSK \"$keyset\" in \"$krname\"");
|
|
return(0);
|
|
}
|
|
%set = %$krec;
|
|
|
|
#
|
|
# Make sure we've got an actual set keyrec and keys.
|
|
#
|
|
if($set{'keyrec_type'} ne 'set')
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$keyset\"'s keyrec is not a set keyrec");
|
|
return(0);
|
|
}
|
|
if(!defined($set{'keys'}) || ($set{'keys'} eq ""))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$krname\" has no keys; unable to check expiration");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Ensure that there's a signing set.
|
|
#
|
|
@signset = split / /, $set{'keys'};
|
|
if(@signset == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$krname\" has no keys; unable to check expiration");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Check each key in the signing set to find the one with the shortest
|
|
# lifespan. We'll calculate rollover times based on that.
|
|
#
|
|
foreach my $keyname (@signset)
|
|
{
|
|
my $keylife; # Key's life value.
|
|
|
|
#
|
|
# Get the key's keyrec hash.
|
|
#
|
|
$khr = keyrec_fullrec($keyname);
|
|
next if(!$khr);
|
|
%kh = %$khr;
|
|
|
|
#
|
|
# Ensure that required keyrec field exists.
|
|
#
|
|
if(!defined($kh{"zsklife"}))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec does not contain a zsklife record");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# If this key's life is shorter than any we've seen, it
|
|
# becomes the new minimum. If this is the first key we're
|
|
# looking at, we'll save it as the minimum.
|
|
#
|
|
$keylife = $kh{'zsklife'};
|
|
if($minlife == -1)
|
|
{
|
|
$minlife = $keylife;
|
|
$minhr = $khr;
|
|
}
|
|
else
|
|
{
|
|
if($keylife < $minlife)
|
|
{
|
|
$minlife = $keylife;
|
|
$minhr = $khr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!defined($minhr))
|
|
{
|
|
rolllog_log(LOG_ALWAYS,$rname,"--------> zsk_expired: couldn't find minimum key keyrec");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Get the minimum key's keyrec hash.
|
|
#
|
|
%kh = %$minhr;
|
|
|
|
#
|
|
# Get the start time on which the expiration time is based.
|
|
#
|
|
if($zrollmethod == $RM_ENDROLL)
|
|
{
|
|
#
|
|
# Ensure that required rollrec field exists.
|
|
#
|
|
if(!defined($rr{'zsk_rollsecs'}))
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"creating new zsk_rollsecs record and forcing ZSK rollover");
|
|
rollstamp($rname,'zsk');
|
|
return(0);
|
|
}
|
|
$starter = $rr{'zsk_rollsecs'};
|
|
}
|
|
elsif($zrollmethod == $RM_KEYGEN)
|
|
{
|
|
#
|
|
# Ensure that required keyrec field exists.
|
|
#
|
|
if(!defined($kh{'keyrec_gensecs'}))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec does not contain a keyrec_gensecs record");
|
|
return(0);
|
|
}
|
|
$starter = $kh{'keyrec_gensecs'};
|
|
}
|
|
elsif($zrollmethod == $RM_STARTROLL)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"RM_STARTROLL not yet implemented");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Don't roll immediately if the rollrec file was newly created.
|
|
#
|
|
if($starter == 0)
|
|
{
|
|
rollstamp($rname,'zsk');
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Get the key's expiration time.
|
|
#
|
|
$rolltime = $starter + $minlife;
|
|
|
|
#
|
|
# Get the current time.
|
|
#
|
|
# $cronus = time;
|
|
$cronus = gmtime(time);
|
|
$cronus = str2time($cronus);
|
|
|
|
#
|
|
# Figure out the log message we should give.
|
|
#
|
|
$waitsecs = $rolltime - $cronus;
|
|
if($waitsecs >= 0)
|
|
{
|
|
$chronostr = timetrans($waitsecs);
|
|
rolllog_log(LOG_EXPIRE,$rname," expiration in $chronostr\n");
|
|
display("expiration \"$rname\" 0 ZSK $waitsecs");
|
|
}
|
|
else
|
|
{
|
|
$waitsecs = $cronus - $rolltime;
|
|
$chronostr = timetrans($waitsecs);
|
|
rolllog_log(LOG_EXPIRE,$rname," expired $chronostr ago\n");
|
|
display("zskphase \"$rname\" 0");
|
|
}
|
|
|
|
#
|
|
# The keyset has expired if the current time has passed the keyset's
|
|
# lifespan.
|
|
# The keyset has not expired if the keyset's lifespan has yet to reach
|
|
# the current time.
|
|
#
|
|
$expired = 1 if($cronus > $rolltime);
|
|
|
|
#
|
|
# If the keyset has not expired and the zone file has been modified,
|
|
# we'll sign the zone file. We won't created any new keys or take
|
|
# any other rollover actions.
|
|
#
|
|
zonemodified($rrr,$rname) if(!$expired);
|
|
|
|
#
|
|
# Return the success/failure indication.
|
|
#
|
|
return($expired);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zsk_phaser()
|
|
#
|
|
# Purpose: Move the specified zone's ZSKs through the appropriate phases.
|
|
#
|
|
sub zsk_phaser
|
|
{
|
|
my $rname = shift; # Zone name.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my $ph; # Zone's current rollover phase.
|
|
|
|
#
|
|
# Get this rollrec record's current phase.
|
|
#
|
|
$ph = $rrr->{'zskphase'};
|
|
|
|
#
|
|
# Work on this rollrec's phase.
|
|
#
|
|
$ph = phasecmd(\&nextphase,$rname,$rrr,'normal','ZSK') if($ph == 0);
|
|
$ph = phasecmd(\&phasewait,$rname,$rrr,'zsk1') if($ph == 1);
|
|
$ph = phasecmd(\&zsk_phase2,$rname,$rrr,'zsk2') if($ph == 2);
|
|
$ph = phasecmd(\&phasewait,$rname,$rrr,'zsk3') if($ph == 3);
|
|
$ph = phasecmd(\&zsk_phase4,$rname,$rrr,'zsk4') if($ph == 4);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zsk_phase2()
|
|
#
|
|
# Purpose: Perform the phase 2 steps of the ZSK rollover. These are:
|
|
#
|
|
# - sign the zone with the KSK and Published ZSK
|
|
# - reload the zone
|
|
# - wait for old zone data to expire
|
|
#
|
|
sub zsk_phase2
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
|
|
my $krf; # Rollrec's keyrec name.
|
|
my %krf; # Keyrec hash.
|
|
my $krr; # Keyrec reference.
|
|
|
|
my $ret; # Result from command executions.
|
|
|
|
#
|
|
# Get the rollrec's associated keyrec file and ensure that it exists.
|
|
#
|
|
$krf = $rr{'keyrec'};
|
|
if(!$krf || ($krf eq ""))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 2: no keyrec for zone specified");
|
|
return(2);
|
|
}
|
|
if(! -e $krf)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 2: keyrec \"$krf\" for zone does not exist");
|
|
return(2);
|
|
}
|
|
|
|
#
|
|
# Get the keyrec.
|
|
#
|
|
$krr = opts_zonekr($krf,$rr{'zonename'});
|
|
%krf = %$krr;
|
|
|
|
#
|
|
# Sign the zone with the Published ZSK.
|
|
#
|
|
$ret = signer($rname,"ZSK phase 2",$krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 2: unable to sign zone with the Published ZSK");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Re-read the keyrec file, since it was rewritten by zonesigner.
|
|
# Also, we'll get rid of any changes we've made to the keyrec.
|
|
# (This *shouldn't* be a problem; further testing will tell...)
|
|
#
|
|
keyrec_discard();
|
|
$ret = keyrec_read($krf);
|
|
if($ret < 0)
|
|
{
|
|
if($ret == -3)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 2: keyname collision in keyrec \"$krf\"; marking $rname as skipped");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 2: unable to read keyrec \"$krf\"; marking $rname as skipped");
|
|
}
|
|
skipnow($rname);
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Update the timestamp in the zone's keyrec.
|
|
#
|
|
keyrec_settime("zone",$rrr->{'zonename'});
|
|
keyrec_write();
|
|
|
|
#
|
|
# Reload the zone.
|
|
#
|
|
$ret = loadzone($rndc,$rname,\%rr,"ZSK phase 2");
|
|
if($ret != 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 2: unable to reload zone, rc - $ret");
|
|
}
|
|
|
|
#
|
|
# Return the next phase number.
|
|
#
|
|
return(3);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zsk_phase4()
|
|
#
|
|
# Purpose: Perform the phase 4 steps of the rollover. These are:
|
|
#
|
|
# - juggle the ZSKs in the zone's keyrec
|
|
# - sign the zone with the KSK and new current ZSK
|
|
# - reload the zone
|
|
# - return the zone to the pre-rollover state
|
|
#
|
|
sub zsk_phase4
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
|
|
my $krf; # Rollrec's keyrec name.
|
|
my $krr; # Keyrec reference.
|
|
|
|
my $ret; # Result from command executions.
|
|
|
|
#
|
|
# Get the rollrec's associated keyrec file and ensure that it
|
|
# exists.
|
|
#
|
|
$krf = $rr{'keyrec'};
|
|
if(!$krf || ($krf eq ""))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 4: no keyrec for zone specified");
|
|
return(-1);
|
|
}
|
|
if(! -e $krf)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 4: keyrec \"$krf\" for zone does not exist");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Get the keyrec.
|
|
#
|
|
rolllog_log(LOG_TMI,$rname,"keyrec file is $krf");
|
|
$krr = opts_zonekr($krf,$rr{'zonename'});
|
|
|
|
#
|
|
# Adjust ZSKs in the zone's keyrec.
|
|
#
|
|
$ret = signer($rname,"ZSK phase 4a",$krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 4: unable to adjust ZSK keyrec");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Sign the zone with the Current ZSK.
|
|
#
|
|
$ret = signer($rname,"ZSK phase 4b",$krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 4: unable to sign zone with the Current ZSK");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Reload the zone.
|
|
#
|
|
$ret = loadzone($rndc,$rname,\%rr,"ZSK phase 4");
|
|
if($ret != 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"ZSK phase 4: unable to reload zone, rc - $ret");
|
|
}
|
|
|
|
#
|
|
# Set a timestamp for the completion of the ZSK roll.
|
|
#
|
|
rollstamp($rname,'zsk');
|
|
clearzoneerr($rname,$rrr);
|
|
return(5);
|
|
}
|
|
|
|
###############################################################################
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ksk_expired()
|
|
#
|
|
# Purpose: This routine determines if the specified zone has an expired
|
|
# KSK and returns a boolean indicating this. The given KSK
|
|
# type is checked for expiration.
|
|
#
|
|
# The zone's keyrec file name is taken from the given rollrec
|
|
# entry. The keyrec file is read and the zone's entry found.
|
|
# The key keyrec of the specified key type (currently, just
|
|
# "kskcur") is pulled from the keyrec file. Each key in the
|
|
# named signing set will be checked.
|
|
#
|
|
# Key expiration is determined by comparing the key keyrec's
|
|
# gensecs field to the current time. The key hasn't expired
|
|
# if the current time is less than the gensecs; the key has
|
|
# expired if the current time is greater than the gensecs.
|
|
#
|
|
sub ksk_expired
|
|
{
|
|
my $rname = shift; # Name of rollrec rec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $keyset = shift; # Key to check.
|
|
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $krname; # Name of keyrec.
|
|
my $krec; # Keyrec reference.
|
|
my %set; # Keyrec hash.
|
|
|
|
my $khr; # Ref to key's hash.
|
|
my %kh; # Key's hash.
|
|
|
|
my @signset; # Key's signing set.
|
|
|
|
my $chronostr; # Text expiration time.
|
|
my $cronus; # Current time.
|
|
my $expired = 0; # Expired-zone flag.
|
|
my $minlife = -1; # Minimum KSK life.
|
|
my $minhr; # Ref to min-key's keyrec.
|
|
my $rolltime; # Time roll should occur.
|
|
my $starter; # Time 0 for calc'ing rolltime.
|
|
my $waitsecs; # Time to expiration.
|
|
|
|
#
|
|
# Get the rollrec's hash.
|
|
#
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# If this zone is in the middle of ZSK rollover, we'll stop
|
|
# working on KSK rollover.
|
|
#
|
|
if($rr{'zskphase'} > 0)
|
|
{
|
|
my $pstr; # Phase string.
|
|
|
|
$pstr = ": " . rollmgr_get_phase('KSK', $rr{'kskphase'});
|
|
$pstr = '' if($pstr eq ": ");
|
|
|
|
rolllog_log(LOG_TMI,$rname,"in ZSK rollover (phase $rr{'zskphase'}$pstr); not attempting KSK rollover");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# If this zone is in the middle of rollover processing, we'll
|
|
# immediately assume the key has expired.
|
|
#
|
|
return(1) if($rr{'kskphase'} > 0);
|
|
|
|
#
|
|
# Get the rollin' key's keyrec for our zone.
|
|
#
|
|
$krname = $rr{'keyrec'};
|
|
$krec = zonekeykr($rname,$rr{'zonename'},$krname,$keyset);
|
|
if($krec == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to find a KSK keyrec for \"$keyset\" in \"$krname\"");
|
|
return(0);
|
|
}
|
|
%set = %$krec;
|
|
|
|
#
|
|
# Make sure we've got an actual set keyrec and keys.
|
|
#
|
|
if($set{'keyrec_type'} ne 'set')
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$keyset\"'s keyrec is not a set keyrec");
|
|
return(0);
|
|
}
|
|
if(!defined($set{'keys'}) || ($set{'keys'} eq ""))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$krname\" has no keys; unable to check expiration");
|
|
zoneerr($rname,$rrr);
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Ensure that there's a signing set.
|
|
#
|
|
@signset = split / /, $set{'keys'};
|
|
if(@signset == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$krname\" has no keys; unable to check expiration");
|
|
zoneerr($rname,$rrr);
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Check each key in the signing set to find the one with the shortest
|
|
# lifespan. We'll calculate rollover times based on that.
|
|
#
|
|
foreach my $keyname (@signset)
|
|
{
|
|
my $keylife; # Key's life value.
|
|
|
|
#
|
|
# Get the key's keyrec hash.
|
|
#
|
|
$khr = keyrec_fullrec($keyname);
|
|
if($khr == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to find key \"$keyname\" in keyrec; this should not have happened");
|
|
zoneerr($rname,$rrr);
|
|
return(0);
|
|
}
|
|
%kh = %$khr;
|
|
|
|
#
|
|
# Ensure that required keyrec field exists.
|
|
#
|
|
if(!defined($kh{"ksklife"}))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec does not contain a ksklife record");
|
|
zoneerr($rname,$rrr);
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# If this key's life is shorter than any we've seen, it
|
|
# becomes the new minimum. If this is the first key we're
|
|
# looking at, we'll save it as the minimum.
|
|
#
|
|
$keylife = $kh{'ksklife'};
|
|
if($minlife == -1)
|
|
{
|
|
$minlife = $keylife;
|
|
$minhr = $khr;
|
|
}
|
|
else
|
|
{
|
|
if($keylife < $minlife)
|
|
{
|
|
$minlife = $keylife;
|
|
$minhr = $khr;
|
|
}
|
|
}
|
|
}
|
|
|
|
#
|
|
# Get the minimum key's keyrec hash.
|
|
#
|
|
%kh = %$minhr;
|
|
|
|
#
|
|
# Get the start time on which the expiration time is based.
|
|
#
|
|
if($krollmethod == $RM_ENDROLL)
|
|
{
|
|
#
|
|
# Ensure that required rollrec field exists.
|
|
#
|
|
if(!defined($rr{'ksk_rollsecs'}))
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"creating new ksk_rollsecs record and forcing KSK rollover");
|
|
rollstamp($rname,'ksk');
|
|
return(0);
|
|
}
|
|
$starter = $rr{'ksk_rollsecs'};
|
|
}
|
|
elsif($krollmethod == $RM_KEYGEN)
|
|
{
|
|
#
|
|
# Ensure that required keyrec field exists.
|
|
#
|
|
if(!defined($kh{'keyrec_gensecs'}))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec does not contain a keyrec_gensecs record");
|
|
return(0);
|
|
}
|
|
$starter = $kh{'keyrec_gensecs'};
|
|
}
|
|
elsif($krollmethod == $RM_STARTROLL)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"RM_STARTROLL not yet implemented");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Don't roll immediately if the rollrec file was newly created.
|
|
#
|
|
if($starter == 0)
|
|
{
|
|
rollstamp($rname,'ksk');
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Get the key's expiration time.
|
|
#
|
|
$rolltime = $starter + $minlife;
|
|
|
|
#
|
|
# Get the current time.
|
|
#
|
|
$cronus = gmtime(time);
|
|
$cronus = str2time($cronus);
|
|
|
|
#
|
|
# Figure out the log message we should give.
|
|
#
|
|
$waitsecs = $rolltime - $cronus;
|
|
if($waitsecs >= 0)
|
|
{
|
|
$chronostr = timetrans($waitsecs);
|
|
rolllog_log(LOG_EXPIRE,$rname," expiration in $chronostr\n");
|
|
display("expiration \"$rname\" 0 KSK $waitsecs");
|
|
}
|
|
else
|
|
{
|
|
$waitsecs = $cronus - $rolltime;
|
|
$chronostr = timetrans($waitsecs);
|
|
rolllog_log(LOG_EXPIRE,$rname," expired $chronostr ago\n");
|
|
display("kskphase \"$rname\" 0");
|
|
}
|
|
|
|
#
|
|
# The key has expired if the current time has passed the key's lifespan.
|
|
# The key has not expired if the key's lifespan has yet to reach the
|
|
# current time.
|
|
#
|
|
$expired = 1 if($cronus > $rolltime);
|
|
|
|
#
|
|
# If the keyset has not expired and the zone file has been modified,
|
|
# we'll sign the zone file. We won't created any new keys or take
|
|
# any other rollover actions.
|
|
#
|
|
zonemodified($rrr,$rname) if(!$expired);
|
|
|
|
#
|
|
# Return the success/failure indication.
|
|
#
|
|
return($expired);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ksk_phaser()
|
|
#
|
|
# Purpose: Move the specified zone's KSKs through the appropriate phases.
|
|
#
|
|
# Phases in rollover:
|
|
# 1 - wait for cache data to expire
|
|
# 2 - generate a new (published) KSK and load zone
|
|
# 3 - wait for the old DNSKEY RRset to expire from caches
|
|
# 4 - transfer new keyset to the parent
|
|
# 5 - wait for parent to publish DS record
|
|
# 6 - wait for cache data to expire
|
|
# 7 - roll the KSKs and load the zone
|
|
#
|
|
#
|
|
sub ksk_phaser
|
|
{
|
|
my $rname = shift; # Zone name.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $ph; # Zone's current rollover phase.
|
|
|
|
#
|
|
# Get this rollrec record's current phase.
|
|
#
|
|
$ph = $rrr->{'kskphase'};
|
|
|
|
#
|
|
# Work on this rollrec's phase.
|
|
#
|
|
$ph = phasecmd(\&nextphase,$rname,$rrr,'normal','KSK') if($ph == 0);
|
|
$ph = phasecmd(\&phasewait, $rname,$rrr,'ksk1') if($ph == 1);
|
|
$ph = phasecmd(\&ksk_phase2,$rname,$rrr,'ksk2') if($ph == 2);
|
|
$ph = phasecmd(\&phasewait, $rname,$rrr,'ksk3') if($ph == 3);
|
|
$ph = phasecmd(\&ksk_phase4,$rname,$rrr,'ksk4') if($ph == 4);
|
|
$ph = phasecmd(\&ksk_phase5,$rname,$rrr,'ksk5') if($ph == 5);
|
|
$ph = phasecmd(\&phasewait, $rname,$rrr,'ksk6') if($ph == 6);
|
|
$ph = phasecmd(\&ksk_phase7,$rname,$rrr,'ksk7') if($ph == 7);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ksk_phase2()
|
|
#
|
|
# Purpose: Perform the phase 2 steps of the KSK rollover. These are:
|
|
# - generate a new KSK to be the Published KSK
|
|
# - add the new Published KSK to the zone file
|
|
# - re-sign the zone file with the Current KSK, the (new)
|
|
# Published KSK, and the Current ZSK
|
|
# - reload the zone file
|
|
# The first three steps are handled by zonesigner.
|
|
#
|
|
sub ksk_phase2
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
|
|
my $krf; # Rollrec's keyrec name.
|
|
my $krr; # Keyrec reference.
|
|
|
|
my $ret; # Result from command executions.
|
|
|
|
#
|
|
# Get the rollrec's associated keyrec file and ensure that it exists.
|
|
#
|
|
$krf = $rr{'keyrec'};
|
|
if(!$krf || ($krf eq ""))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 2: no keyrec for zone specified");
|
|
return(-1);
|
|
}
|
|
if(! -e $krf)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 2: keyrec \"$krf\" for zone does not exist");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Get the keyrec.
|
|
#
|
|
$krr = opts_zonekr($krf,$rr{'zonename'});
|
|
|
|
#
|
|
# Sign the zone with a new Published KSK.
|
|
#
|
|
$ret = signer($rname,"KSK phase 2",$krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 2: unable to sign zone with the Published KSK");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Reload the zone.
|
|
#
|
|
$ret = loadzone($rndc,$rname,\%rr,"KSK phase 2");
|
|
if($ret != 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 2: unable to reload zone, rc - $ret");
|
|
}
|
|
|
|
#
|
|
# On to the phase 3.
|
|
#
|
|
return(3);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ksk_phase4()
|
|
#
|
|
# Purpose: Perform the phase 4 steps of the KSK rollover. These are:
|
|
# - notify the admin that the new keyset should be
|
|
# transferred to the parent zone
|
|
#
|
|
# This is currently handled by telling the admin to transfer
|
|
# the keyset. It would be nice to have an automated method
|
|
# of keyset transfer; one may be added in future.
|
|
#
|
|
#
|
|
sub ksk_phase4
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my $ret; # Return code from mail
|
|
my $auto = 0; # Automatic-transfer flag.
|
|
|
|
if($auto)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 4: automatic keyset transfer not yet supported");
|
|
return(-1);
|
|
}
|
|
elsif(($dtconf{'admin-email'} eq 'nomail') ||
|
|
($rrr->{'administrator'} eq 'nomail'))
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"KSK phase 4: admin must transfer keyset");
|
|
}
|
|
else
|
|
{
|
|
my $subject; # Subject for email to admin.
|
|
my $msg; # Message to admin.
|
|
my $admin; # Zone's administrator.
|
|
|
|
$subject = "assistance needed with KSK rollover of zone $rname";
|
|
$msg = "
|
|
The zone \"$rname\" is in the middle of KSK rollover. In order for rollover
|
|
to continue, its keyset must be transferred to its parent.";
|
|
|
|
#
|
|
# If this zone has its own administrator listed, we won't use
|
|
# the default.
|
|
#
|
|
if(exists($rrr->{'administrator'}))
|
|
{
|
|
$admin = $rrr->{'administrator'};
|
|
$ret = dt_adminmail($subject,$msg,$admin);
|
|
}
|
|
else
|
|
{
|
|
$ret = dt_adminmail($subject,$msg);
|
|
}
|
|
|
|
if($ret == 1)
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"KSK phase 4: admin notified to transfer keyset");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"KSK phase 4: admin must transfer keyset");
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 4: invalid admin; unable to notify about transferring keyset");
|
|
}
|
|
}
|
|
|
|
#
|
|
# Pressing on to phase 5.
|
|
#
|
|
return(5);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ksk_phase5()
|
|
#
|
|
# Purpose: Perform the phase 5 steps of the KSK rollover. These are:
|
|
# - wait for the parent to publish the DS record
|
|
#
|
|
# This is currently handled by the admin telling us that the
|
|
# DS record has been published. It would be nice to have an
|
|
# automated method of determining this; one may be added in
|
|
# future.
|
|
#
|
|
sub ksk_phase5
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my $auto = 0; # Automatic-transfer flag.
|
|
|
|
if($auto)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 5: automatic DS-record determination not yet supported");
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"KSK phase 5: waiting for parental publication of DS record");
|
|
}
|
|
|
|
return(5);
|
|
# return(6);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ksk_phase7()
|
|
#
|
|
# Purpose: Perform the phase 7 steps of the KSK rollover. These are:
|
|
# - delete the Current KSK from the zone file
|
|
# - move the Published KSK to be the Current KSK
|
|
# - sign the zone file with the (new) Current KSK
|
|
# - load the zone
|
|
# - archive keys that need to be archived
|
|
# - move to phase 0
|
|
# - save a timestamp for rollover completion
|
|
#
|
|
# These first three steps are handled by zonesigner.
|
|
#
|
|
sub ksk_phase7
|
|
{
|
|
my $rname = shift; # Name of rolling rollrec.
|
|
my $rrr = shift; # Reference to zone's rollrec record.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
my $krf; # Rollrec's keyrec name.
|
|
my $krr; # Keyrec reference.
|
|
my $zname; # Name of rolling zone.
|
|
my $keyarch_cmd; # Keyarch command string.
|
|
|
|
my $ret; # Result from command executions.
|
|
|
|
#
|
|
# Get the rollrec's associated keyrec file and ensure that it exists.
|
|
#
|
|
$krf = $rr{'keyrec'};
|
|
if(!$krf || ($krf eq ''))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 7: no keyrec for zone specified");
|
|
return(-1);
|
|
}
|
|
if(! -e $krf)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 7: keyrec \"$krf\" for zone does not exist");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Get the keyrec.
|
|
#
|
|
$krr = opts_zonekr($krf,$rr{'zonename'});
|
|
|
|
#
|
|
# Roll the Published KSK to the Current KSK.
|
|
#
|
|
$ret = signer($rname,'KSK phase 7',$krr);
|
|
if($ret)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 7: unable to roll the Published KSK to the Current KSK");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Reload the zone.
|
|
#
|
|
$ret = loadzone($rndc,$rname,\%rr,"KSK phase 7");
|
|
if($ret != 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 7: unable to reload zone, rc - $ret");
|
|
}
|
|
|
|
#
|
|
# Set up the keyarch command we'll be executing.
|
|
#
|
|
$zname = $rr{'zonename'};
|
|
$keyarch_cmd = "$keyarch -dtconf $dtcf -zone $zname $krf -verbose";
|
|
|
|
rolllog_log(LOG_TMI,$rname,"keyarch: running <$keyarch_cmd>");
|
|
$ret = runner($rname,"$keyarch_cmd",$krf,1);
|
|
if($ret != 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"KSK phase 7: unable to archive KSK keys, rc - $ret");
|
|
|
|
zoneerr($rname,$rrr);
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"KSK phase 7: zone, key files archived");
|
|
clearzoneerr($rname,$rrr);
|
|
}
|
|
|
|
#
|
|
# Set a timestamp for the completion of the KSK roll.
|
|
#
|
|
rollstamp($rname,'ksk');
|
|
|
|
#
|
|
# Returning to normal rollover state.
|
|
#
|
|
return(8);
|
|
}
|
|
|
|
###############################################################################
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: maxttl()
|
|
#
|
|
# Purpose: Calculate the zone's maximum TTL value, based on the largest
|
|
# TTL in its zone file.
|
|
#
|
|
sub maxttl
|
|
{
|
|
my $signdb = shift; # Signed zone's db filename.
|
|
|
|
my $rrsref; # Reference to zone's resource records.
|
|
my @rrs; # Zone's resource records.
|
|
my $numrrs; # Number of resource records in zone.
|
|
my $rrref; # Reference to a resource record.
|
|
my %rr; # A resource record to examine.
|
|
my $maxttl = -1; # Zone's maximum time-to-live value.
|
|
my $ttl; # Time-to-live value from a record.
|
|
|
|
#
|
|
# Ensure the zone file exists.
|
|
#
|
|
if(! -e $signdb)
|
|
{
|
|
rolllog_log(LOG_ERR,$signdb,"zone file \"$signdb\" does not exist");
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Get the zone's resource record collection.
|
|
#
|
|
$rrsref = dt_parse_zonefile(file => $signdb);
|
|
|
|
#
|
|
# Get the number of resource records in the zone.
|
|
#
|
|
@rrs = @$rrsref;
|
|
$numrrs = @rrs;
|
|
|
|
#
|
|
# Look at each resource record and save the largest value.
|
|
#
|
|
for(my $ind=0;$ind<$numrrs;$ind++)
|
|
{
|
|
$rrref = $rrs[$ind];
|
|
%rr = %$rrref;
|
|
|
|
$ttl = $rr{'ttl'};
|
|
$maxttl = $ttl if($ttl > $maxttl);
|
|
}
|
|
|
|
#
|
|
# Return the zone's maximum TTL value to our caller.
|
|
#
|
|
return($maxttl);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: ttlexpire()
|
|
#
|
|
# Purpose: Returns a boolean indicating if we can move to the next phase
|
|
# of rollover. This is calculated by adding twice the TTL
|
|
# length to the time we entered this phase. If this is greater
|
|
# than the current time, then we must wait a bit longer. If
|
|
# this is less than the current time, then we can proceed to
|
|
# the next rollover phase.
|
|
#
|
|
# Return Values:
|
|
# 0 - Zone's expiry date has not passed.
|
|
# 1 - Zone's expiry date has passed.
|
|
#
|
|
sub ttlexpire
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $phase = shift; # Zone's current phase.
|
|
my $keytype = shift; # Type of key.
|
|
|
|
my $phasedesc = rollmgr_get_phase($keytype, $phase);
|
|
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $zonefile; # Zone file.
|
|
my $maxttl; # Zone's maximum TTL value.
|
|
my $phstart; # Start of zone's phase.
|
|
|
|
my $curtime; # Current time string.
|
|
my $curtempus; # Current time in seconds.
|
|
my $endtempus; # End-date in seconds.
|
|
|
|
my $timediff; # Time until expiration.
|
|
my $tdstr; # String of timediff.
|
|
|
|
#
|
|
# Get a few fields from the rollrec.
|
|
#
|
|
%rr = %$rrr;
|
|
$zonefile = $rr{'zonefile'};
|
|
$phstart = $rr{'phasestart'};
|
|
|
|
#
|
|
# Get the signed zonefile's maximum TTL and save it into the
|
|
# rollrec. We'll then double it as a safety margin.
|
|
#
|
|
$maxttl = maxttl($zonefile);
|
|
rollrec_setval($rname,"maxttl",$maxttl);
|
|
$maxttl *= 2;
|
|
|
|
#
|
|
# Get the current time in seconds.
|
|
#
|
|
$curtime = gmtime;
|
|
$curtempus = str2time($curtime);
|
|
|
|
#
|
|
# Get the phase's end time by adding its start time (in seconds)
|
|
# to the TTL.
|
|
#
|
|
$endtempus = str2time($phstart);
|
|
$endtempus += $maxttl;
|
|
|
|
#
|
|
# Give any desired verbose output.
|
|
#
|
|
if($verbose)
|
|
{
|
|
my $endtime; # End-date string.
|
|
|
|
$endtime = ctime($endtempus);
|
|
chomp $endtime;
|
|
|
|
rolllog_log(LOG_TMI,$rname,"$keytype phase $phase rollover TTL check");
|
|
rolllog_log(LOG_TMI,$rname,"$keytype phase $phase endtime\t$endtime");
|
|
rolllog_log(LOG_TMI,$rname,"$keytype phase $phase curtime\t$curtime");
|
|
}
|
|
|
|
#
|
|
# Return success if we've passed the zone's adjusted end-date.
|
|
# Return failure (not expired) if the zone's adjusted end-date
|
|
# is in the future.
|
|
#
|
|
return(1) if($curtempus >= $endtempus);
|
|
|
|
$timediff = $endtempus - $curtempus;
|
|
$tdstr = timetrans($timediff);
|
|
|
|
$phasedesc = " ($phasedesc)";
|
|
$phasedesc = '' if($phasedesc eq ' ()');
|
|
|
|
rolllog_log(LOG_INFO,$rname,"$keytype phase $phase$phasedesc; cache expires in $tdstr");
|
|
display("expiration \"$rname\" $phase $keytype $timediff");
|
|
|
|
return(0)
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: holddownexpire()
|
|
#
|
|
# Purpose: Returns a boolean indicating if we can move to the next phase
|
|
# of rollover based on the hold-down timer of RFC5011,
|
|
# which is 30 days long.
|
|
#
|
|
# Return Values:
|
|
# 0 - Zone's hold-down timer is still active.
|
|
# 1 - Zone's hold-down timer date/time has passed.
|
|
#
|
|
sub holddownexpire
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $phase = shift; # Zone's current phase.
|
|
my $keytype = shift; # Type of key.
|
|
|
|
my $phasedesc = rollmgr_get_phase($keytype, $phase);
|
|
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $zonefile; # Zone file.
|
|
my $maxttl; # Zone's maximum TTL value.
|
|
my $phstart; # Start of zone's phase.
|
|
my $holddowntime; # Hold-down time.
|
|
|
|
my $curtime; # Current time string.
|
|
my $curtempus; # Current time in seconds.
|
|
my $endtempus; # End-date in seconds.
|
|
|
|
my $timediff; # Time until expiration.
|
|
my $tdstr; # String of timediff.
|
|
|
|
my $addholddown;
|
|
|
|
#
|
|
# This is only needed for phase 3 (currently, at least.)
|
|
#
|
|
return(1) if($phase != 3);
|
|
|
|
#
|
|
# Get a few fields from the rollrec.
|
|
#
|
|
%rr = %$rrr;
|
|
$zonefile = $rr{'zonefile'};
|
|
$phstart = $rr{'phasestart'};
|
|
|
|
#
|
|
# By default, we use a hold-down time of 60 days which is twice what
|
|
# is required for validators to use as a add-hold-time (RFC5011).
|
|
# (Ideally this should be longer and can be set by the user if needed.)
|
|
#
|
|
$holddowntime = dt_parse_duration($rr{'holddowntime'});
|
|
|
|
#
|
|
# Default to 60 days.
|
|
# lsdnssec should change too if this default is changed.
|
|
#
|
|
$holddowntime ||= 2 * 30 * 24 * 60 * 60;
|
|
|
|
#
|
|
# Get the current time in seconds.
|
|
#
|
|
$curtime = gmtime;
|
|
$curtempus = str2time($curtime);
|
|
|
|
#
|
|
# Get the phase's end time (in seconds) and add the hold-down.
|
|
#
|
|
$endtempus = str2time($phstart);
|
|
$endtempus += $holddowntime;
|
|
|
|
#
|
|
# Give any desired verbose output.
|
|
#
|
|
if($verbose)
|
|
{
|
|
my $endtime; # End-date string.
|
|
|
|
$endtime = ctime($endtempus);
|
|
chomp $endtime;
|
|
|
|
rolllog_log(LOG_TMI,$rname,"$keytype phase $phase hold-down time check");
|
|
rolllog_log(LOG_TMI,$rname,"$keytype phase $phase endtime\t$endtime");
|
|
rolllog_log(LOG_TMI,$rname,"$keytype phase $phase curtime\t$curtime");
|
|
}
|
|
|
|
#
|
|
# Return success if we've passed the zone's adjusted end-date.
|
|
# Return failure (not expired) if the zone's adjusted end-date
|
|
# is in the future.
|
|
#
|
|
return(1) if($curtempus >= $endtempus);
|
|
|
|
$timediff = $endtempus - $curtempus;
|
|
$tdstr = timetrans($timediff);
|
|
|
|
$phasedesc = " ($phasedesc)";
|
|
$phasedesc = '' if($phasedesc eq ' ()');
|
|
|
|
rolllog_log(LOG_INFO,$rname,"$keytype phase $phase$phasedesc; hold-down timer expires in $tdstr");
|
|
display("holddown \"$rname\" $phase $keytype $timediff");
|
|
|
|
return(0)
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: rollstamp()
|
|
#
|
|
# Purpose: This routine sets the timestamp records for a KSK or
|
|
# ZSK roll in the rollrec file.
|
|
#
|
|
sub rollstamp
|
|
{
|
|
my $rname = shift; # Rollrec name.
|
|
my $prefix = shift; # Key-type prefix.
|
|
|
|
my $date; # Date key.
|
|
my $secs; # Seconds key.
|
|
|
|
my $timesecs; # Current-time seconds count.
|
|
my $timestr; # Current-time string.
|
|
|
|
#
|
|
# Build the rollrec keys we'll be using.
|
|
#
|
|
$date = $prefix . "_rolldate";
|
|
$secs = $prefix . "_rollsecs";
|
|
|
|
#
|
|
# Get the timestamps.
|
|
#
|
|
# $timesecs = time();
|
|
# $timestr = gmtime($timesecs);
|
|
$timestr = gmtime(time);
|
|
$timesecs = str2time($timestr);
|
|
|
|
#
|
|
# Set the rollrec records to now and write the file.
|
|
#
|
|
rollrec_setval($rname,$date,$timestr);
|
|
rollrec_setval($rname,$secs,$timesecs);
|
|
rollrec_write();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: phasecmd()
|
|
#
|
|
# Purpose: Run a list of commands for this rollover phase. If a list is
|
|
# not defined for this phase, then we'll use the default action.
|
|
#
|
|
sub phasecmd
|
|
{
|
|
my $phasefunc = shift; # Reference to phase function.
|
|
my $rname = shift; # Zone name.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $phase = shift; # Phase we're handling.
|
|
my $ptaux = shift; # Auxiliary phase type.
|
|
|
|
my $progkey = "prog-$phase"; # Config. key for phase's programs.
|
|
my $cmdlist; # Execution flag.
|
|
my @cmds; # List of commands to run for phase.
|
|
my $ret; # Return value from phase function.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
my $cmdargs; # Arguments to the command.
|
|
my $phasetype; # Phase type.
|
|
my $phasenum; # Phase number.
|
|
my $newphase; # New phase number.
|
|
|
|
#
|
|
# Get the command list for this rollover phase. Force the default
|
|
# if a command list isn't defined for the phase.
|
|
#
|
|
if(!defined($dtconf{$progkey}) || ($dtconf{$progkey} eq ''))
|
|
{
|
|
$cmdlist = 'default';
|
|
}
|
|
else
|
|
{
|
|
$cmdlist = $dtconf{$progkey}
|
|
}
|
|
@cmds = split /!/, $cmdlist;
|
|
|
|
#
|
|
# Break out the phase atoms. If this is a normal phase, then
|
|
# we'll be alchemists and transmute the atoms we need.
|
|
#
|
|
if($phase eq 'normal')
|
|
{
|
|
$phasetype = 'normal';
|
|
$phasenum = 1;
|
|
}
|
|
else
|
|
{
|
|
$phase = lc($phase);
|
|
$phase =~ /([kz]sk)(\d)/;
|
|
$phasetype = $1;
|
|
$phasenum = $2;
|
|
|
|
#
|
|
# Set a variable for phasewait().
|
|
#
|
|
$ptaux = $phasetype if(! defined($ptaux));
|
|
}
|
|
|
|
#
|
|
# Run the commands defined for this phase. The default commands
|
|
# are handled internally to rollerd.
|
|
#
|
|
foreach my $cmd (@cmds)
|
|
{
|
|
#
|
|
# Get rid of any leading or trailing blanks.
|
|
#
|
|
$cmd =~ s/^\s*(.*?)\s*$/$1/;
|
|
|
|
#
|
|
# Take this phase's normal rollover action if the command
|
|
# is "default".
|
|
#
|
|
if($cmd =~ /^default$/i)
|
|
{
|
|
#
|
|
# Run the phase-specific routine.
|
|
#
|
|
# When this change was made, nextphase() (and the old
|
|
# kskphase() and zskphase()) were the only phasefuncs
|
|
# that used the third and fourth arguments. The fourth
|
|
# was originally $phasetype, but it's being changed to
|
|
# $ptaux.
|
|
#
|
|
$ret = $phasefunc->($rname,$rrr,$phasenum,$ptaux);
|
|
return($phasenum) if($ret < 0);
|
|
|
|
#
|
|
# This is a special case for when we're in normal
|
|
# rollover.
|
|
# (Yes, in this situation, normal is a special case.)
|
|
#
|
|
$phasenum = 0 if($phase eq '0');
|
|
|
|
#
|
|
# Save the return code as our new phase.
|
|
#
|
|
$newphase = $ret;
|
|
}
|
|
else
|
|
{
|
|
#
|
|
# Set up the arguments for the command.
|
|
#
|
|
$cmdargs = "$rr{'zonename'} $phase $rname $rollrecfile $rr{'keyrec'}";
|
|
|
|
#
|
|
# Execute the phase's locally defined program.
|
|
#
|
|
$ret = localprog($rname,$cmd,$cmdargs,$phase);
|
|
|
|
#
|
|
# Stay in this phase if this command didn't succeed.
|
|
#
|
|
return($phasenum) if($ret != 0);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
#
|
|
# If we've reached the final rollover phase, we'll go to non-rollover.
|
|
#
|
|
if(($phase eq 'ksk7') || ($phase eq 'zsk4'))
|
|
{
|
|
$newphase = 0;
|
|
}
|
|
|
|
#
|
|
# Special handling if the phase has changed, followed by special
|
|
# handling if it hasn't.
|
|
#
|
|
if($phasenum != $newphase)
|
|
{
|
|
#
|
|
# Set the new KSK phase.
|
|
#
|
|
if($phase =~ /^ksk/)
|
|
{
|
|
nextphase($rname,$rrr,$newphase,'KSK');
|
|
}
|
|
|
|
#
|
|
# Set the new ZSK phase.
|
|
#
|
|
if($phase =~ /^zsk/)
|
|
{
|
|
nextphase($rname,$rrr,$newphase,'ZSK');
|
|
}
|
|
|
|
zonemodified($rrr,$rname) if($newphase == 0);
|
|
}
|
|
else
|
|
{
|
|
#
|
|
# Re-sign the zone file if it has been modified more recently
|
|
# than the signed zone file.
|
|
#
|
|
zonemodified($rrr,$rname);
|
|
}
|
|
|
|
#
|
|
# Return the phase number we should be in.
|
|
#
|
|
return($newphase);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: phasewait()
|
|
#
|
|
# Purpose: Check if this zone's rollover wait phase is over.
|
|
# The zone's phase number -- current or new -- is returned.
|
|
# KSKs will also be checked for trust-anchorship and if the
|
|
# hold-down timer has expired.
|
|
#
|
|
sub phasewait
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $phase = shift; # Rollover phase.
|
|
my $phasetype = shift; # Rollover phase type.
|
|
|
|
#
|
|
# Make sure we've got the latest and greatest rollrec.
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
|
|
#
|
|
# Check if we can go to the next rollover phase. If not, we'll
|
|
# go to the next rollrec entry and return to this later.
|
|
#
|
|
if($phasetype =~ /zsk/i)
|
|
{
|
|
if(!ttlexpire($rname,$rrr,$phase,'ZSK'))
|
|
{
|
|
return($phase);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return($phase) if(!ttlexpire($rname,$rrr,$phase,'KSK'));
|
|
return($phase) if((boolconvert($rrr->{'istrustanchor'}) == 1) &&
|
|
!holddownexpire($rname,$rrr,$phase));
|
|
}
|
|
|
|
#
|
|
# Return the next phase number.
|
|
#
|
|
return($phase + 1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: nextphase()
|
|
#
|
|
# Purpose: Moves a rollrec into the next rollover phase, setting both the
|
|
# phase number and the phase start time.
|
|
#
|
|
sub nextphase
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $rrr = shift; # Rollrec reference.
|
|
my $phase = shift; # New phase.
|
|
my $phasetype = shift; # Type of rollover.
|
|
|
|
my $phasedesc; # Human phase descr.
|
|
my $ptlc; # Lowercase phasetype.
|
|
my $phasekey; # Phase hashkey.
|
|
my $phaselife; # Life hashkey.
|
|
my $curphase; # Current phase hashkey.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
|
|
my $krname; # Name of key's rollrec.
|
|
my $krec; # Key's keyrec.
|
|
|
|
my $setrec; # Set's keyrec.
|
|
my $setlist; # Set's key list.
|
|
|
|
my $exptime; # Key's time to expiry.
|
|
my $chronostr; # Pretty rollover time.
|
|
|
|
#
|
|
# Set up the phase description.
|
|
#
|
|
$phasedesc = rollmgr_get_phase($phasetype,$phase);
|
|
$phasedesc = " ($phasedesc)";
|
|
$phasedesc = '' if($phasedesc eq ' ()');
|
|
|
|
#
|
|
# Set up some phasetype-based hash keys.
|
|
#
|
|
$ptlc = lc($phasetype);
|
|
$phasekey = $ptlc . 'phase';
|
|
$phaselife = $ptlc . 'life';
|
|
$curphase = $ptlc . 'cur';
|
|
|
|
#
|
|
# Give a log message about this rollover phase.
|
|
#
|
|
if($phase == 1)
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"starting $phasetype rollover");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"moving to $phasetype phase $phase$phasedesc");
|
|
}
|
|
|
|
#
|
|
# This is the source of the log messages that look like
|
|
# "KSK phase 3" and "ZSK phase 4".
|
|
#
|
|
rolllog_log(LOG_PHASE,$rname,"$phasetype phase $phase$phasedesc");
|
|
|
|
#
|
|
# Get the latest and greatest rollrec file.
|
|
#
|
|
rollrec_close();
|
|
rollrec_read($rollrecfile);
|
|
|
|
#
|
|
# Change the zone's phase and plop it on disk.
|
|
#
|
|
$rrr->{$phasekey} = $phase;
|
|
rollrec_setval($rname,$phasekey,$phase);
|
|
rollrec_settime($rname);
|
|
rollrec_write();
|
|
rollrec_close();
|
|
rollrec_read($rollrecfile);
|
|
|
|
#
|
|
# Get the rollin' key's keyrec for our zone.
|
|
#
|
|
$krname = $rr{'keyrec'};
|
|
$setrec = zonekeykr($rname,$rr{'zonename'},$krname,$curphase);
|
|
if($setrec == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to find a keyrec for the $curphase signing set in \"$krname\"");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Make sure we've got an actual set keyrec and keys.
|
|
#
|
|
if($setrec->{'keyrec_type'} ne 'set')
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$krname\"'s keyrec is not a set keyrec; unable to move to $phasetype phase $phase");
|
|
return(0);
|
|
}
|
|
if(!defined($setrec->{'keys'}) || ($setrec->{'keys'} eq ""))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"\"$krname\" has no keys; unable to move to $phasetype phase $phase");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Find the key with the shortest lifetime.
|
|
#
|
|
$setlist = $setrec->{'keys'};
|
|
foreach my $key (split / /, $setlist)
|
|
{
|
|
my $lifetime = keyrec_recval($key,$phaselife);
|
|
if(($exptime == 0) || ($lifetime < $exptime))
|
|
{
|
|
$exptime = $lifetime;
|
|
}
|
|
}
|
|
|
|
#
|
|
# Send phase info to the display program.
|
|
#
|
|
$exptime = ($rrr->{'maxttl'} * 2) if($phase != 0);
|
|
display("$phasekey \"$rname\" $phase $exptime");
|
|
|
|
$chronostr = timetrans($exptime);
|
|
rolllog_log(LOG_INFO,$rname," $phasetype expiration in $chronostr");
|
|
|
|
#
|
|
# Reset the phasestart field if we've completed a rollover cycle.
|
|
#
|
|
if($phase == 0)
|
|
{
|
|
rollrec_settime($rname);
|
|
rollrec_write();
|
|
rollrec_close();
|
|
rollrec_read($rollrecfile);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: localprog()
|
|
#
|
|
# Purpose: This routine runs a locally-configured command as part or all
|
|
# of a rollover phase. The programs must be configured in the
|
|
# dnssec-tools.conf file in order to be run here.
|
|
#
|
|
sub localprog
|
|
{
|
|
my $rname = shift; # Rollrec name.
|
|
my $cmd = shift; # Command.
|
|
my $cmdargs = shift; # Command arguments.
|
|
my $phase = shift; # Phase we're in.
|
|
my $out = ''; # Command output.
|
|
my $ret; # Execution return code.
|
|
|
|
#
|
|
# Tell the world about this command.
|
|
#
|
|
rolllog_log(LOG_INFO,$rname,"executing local program \"$cmd $cmdargs\"");
|
|
|
|
#
|
|
# Execute the local command. We'll save the stdout and stderr output
|
|
# in case it's needed later. (Well..., we don't do anything with it
|
|
# right now, but someone might someday want it.
|
|
#
|
|
open(LPROG,"$cmd $cmdargs 2>&1 |");
|
|
while(<LPROG>)
|
|
{
|
|
$out .= $_;
|
|
}
|
|
chomp $out;
|
|
close(LPROG);
|
|
$ret = $? >> 8;
|
|
|
|
#
|
|
# Give a message based on return code.
|
|
#
|
|
if($ret > 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"$phase command $cmd returned error $ret");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"$phase command $cmd succeeded");
|
|
}
|
|
|
|
return($ret);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: signer()
|
|
#
|
|
# Purpose: Signs a zone with a specified ZSK.
|
|
#
|
|
# On success, the return value of the zone-signing command
|
|
# is returned.
|
|
# On failure, "" is returned.
|
|
#
|
|
sub signer
|
|
{
|
|
my $rname = shift; # Name of rollrec.
|
|
my $zsflag = shift; # Flag for key generation.
|
|
my $krr = shift; # Reference to zone's keyrec.
|
|
|
|
my $rrr; # Reference to zone's rollrec.
|
|
my $zsargs; # Zonesigner args from rollrec.
|
|
|
|
my $zonefile; # Zone file from keyrec.
|
|
my $zonesigned; # Signed zone file from keyrec.
|
|
my $cmdstr; # Command to be executed.
|
|
my $initial = 0; # Initial-signing flag.
|
|
my $signonly = ''; # Sign-only flag.
|
|
|
|
#
|
|
# If we've been requested to sign a quiescent zone, we'll set a
|
|
# flag and remove the marker from the phase indicator.
|
|
#
|
|
if($zsflag =~ / -signonly/)
|
|
{
|
|
$signonly = '-signonly';
|
|
$zsflag =~ s/ -signonly//;
|
|
}
|
|
|
|
#
|
|
# Convert the caller's version of $zsflag into what it actually
|
|
# means for the zonesigner execution.
|
|
#
|
|
if($zsflag eq 'KSK phase 2')
|
|
{
|
|
$zsflag = '-newpubksk';
|
|
}
|
|
elsif($zsflag eq 'KSK phase 7')
|
|
{
|
|
$zsflag = '-rollksk';
|
|
}
|
|
elsif($zsflag eq 'ZSK phase 2' || $zsflag eq 'ZSK phase 3')
|
|
{
|
|
$zsflag = '-usezskpub';
|
|
}
|
|
elsif($zsflag eq 'ZSK phase 4')
|
|
{
|
|
signer($rname,'ZSK phase 4a',$krr);
|
|
signer($rname,'ZSK phase 4b',$krr);
|
|
return;
|
|
}
|
|
elsif($zsflag eq 'ZSK phase 4a')
|
|
{
|
|
$zsflag = '-rollzsk';
|
|
}
|
|
elsif($zsflag eq 'ZSK phase 4b')
|
|
{
|
|
$zsflag = '';
|
|
}
|
|
elsif($zsflag eq 'always-sign')
|
|
{
|
|
$zsflag = '-usezskpub';
|
|
}
|
|
elsif($zsflag eq 'initial')
|
|
{
|
|
$zsflag = '-genkeys';
|
|
$initial = 1;
|
|
}
|
|
elsif($zsflag =~ /[KZ]SK phase [013567]/)
|
|
{
|
|
$zsflag = '';
|
|
}
|
|
|
|
#
|
|
# Get the rollrec and any user-specified zonesigner arguments
|
|
# for this zone.
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
$zsargs = $rrr->{'zsargs'};
|
|
|
|
#
|
|
# If there are any user-specified zonesigner arguments, add them
|
|
# to the zonesigner options. If there aren't any, we'll add
|
|
# any global zonesigner args (from rollerd's -zsargs option).
|
|
#
|
|
if($zsargs ne '')
|
|
{
|
|
$zsflag = "$zsflag $zsargs";
|
|
}
|
|
else
|
|
{
|
|
$zsflag .= " $gzsargs" if($gzsargs ne '');
|
|
}
|
|
|
|
#
|
|
# If the -zone flag wasn't specified, we'll force it in here.
|
|
#
|
|
if($zsflag !~ /-zone/)
|
|
{
|
|
$zsflag .= " -zone $rrr->{'zonename'}";
|
|
}
|
|
|
|
#
|
|
# If the -krf flag wasn't specified, we'll force it in here.
|
|
#
|
|
if($zsflag !~ /-krf/)
|
|
{
|
|
$zsflag .= " -krf $rrr->{'keyrec'}";
|
|
}
|
|
|
|
#
|
|
# If the -krf flag wasn't specified, we'll force it in here.
|
|
#
|
|
if($signonly)
|
|
{
|
|
$zsflag .= " -signonly";
|
|
}
|
|
|
|
#
|
|
# Set up a few data for the zonesigner command. Normally, these
|
|
# will be taken from the keyrec file. In the unusual(?) case of
|
|
# needing an initial signing, these will be generated.
|
|
#
|
|
if($initial)
|
|
{
|
|
$zonefile = $rrr->{'zonename'};
|
|
$zonesigned = '';
|
|
}
|
|
else
|
|
{
|
|
#
|
|
# Dig a few data out of the zone's keyrec file.
|
|
#
|
|
$zonefile = getkrval($rname,$krr,"zonefile");
|
|
return("") if($zonefile eq "");
|
|
|
|
$zonesigned = getkrval($rname,$krr,"signedzone");
|
|
return("") if($zonesigned eq "");
|
|
}
|
|
|
|
#
|
|
# Build the command to execute.
|
|
#
|
|
$cmdstr = "$zonesigner -rollmgr rollerd -dtconfig $dtcf $zsflag $zonefile $zonesigned";
|
|
|
|
rolllog_log(LOG_INFO,$rname,"executing \"$cmdstr\"");
|
|
|
|
#
|
|
# Have zonesigner sign the zone for us.
|
|
#
|
|
$ret = runner($rname,$cmdstr,$rrr->{'keyrec'},0);
|
|
if($ret != 0)
|
|
{
|
|
#
|
|
# Error logging is done in runner(), rather than here
|
|
# or in zoneerr().
|
|
#
|
|
skipnow($rname);
|
|
zoneerr($rname,$rrr);
|
|
}
|
|
else
|
|
{
|
|
$rrr->{'signed'} = 1;
|
|
$wassigned = 1;
|
|
}
|
|
|
|
return($ret);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: runner()
|
|
#
|
|
# Purpose: This routine executes another command.
|
|
# This other command is almost certainly going to be zonesigner.
|
|
#
|
|
sub runner
|
|
{
|
|
my $rname = shift; # Name of rollrec rec.
|
|
my $cmd = shift; # Command to execute.
|
|
my $krf = shift; # Zone's keyrec file.
|
|
my $negerrflag = shift; # Only-negative-error flag.
|
|
|
|
my $ret; # Command's return code.
|
|
my $out = ''; # Command's output.
|
|
|
|
#
|
|
# Close the current keyrec file.
|
|
#
|
|
keyrec_discard();
|
|
|
|
#
|
|
# Execute the specific command.
|
|
#
|
|
rolllog_log(LOG_TMI,$rname,"executing \"$cmd\"");
|
|
|
|
#
|
|
# Execute the given command. We'll save the stdout and stderr
|
|
# output in case of error.
|
|
#
|
|
$runerr = 0;
|
|
open(ZS,"$cmd 2>&1 |") || runerr();
|
|
while(<ZS>)
|
|
{
|
|
$out .= $_;
|
|
}
|
|
chomp $out;
|
|
close(ZS);
|
|
$ret = $? >> 8;
|
|
|
|
#
|
|
# If the error flag is set and the command exited with an error,
|
|
# we'll log the output.
|
|
#
|
|
if((! $negerrflag && ($runerr != 0)) ||
|
|
( $negerrflag && ($runerr < 0)))
|
|
{
|
|
$out =~ s/^ if zonesigner appears hung, .*\n$//ms;
|
|
$out =~ s/^ \(see the \"Entropy\" section .*\n$//ms;
|
|
|
|
rolllog_log(LOG_ERR,$rname,"execution error for command \"$cmd\"");
|
|
rolllog_log(LOG_ERR,$rname,"error return - $ret");
|
|
rolllog_log(LOG_ERR,$rname,"error output - \"$out\"");
|
|
}
|
|
|
|
#
|
|
# Re-read current keyrec file and return a success/fail indicator.
|
|
#
|
|
keyrec_read($krf);
|
|
return($runerr);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: runerr()
|
|
#
|
|
# Purpose: This routine sets a global flag indicating an error has
|
|
# been encountered when running an external command.
|
|
#
|
|
sub runerr
|
|
{
|
|
$runerr = 1;
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zonekeykr()
|
|
#
|
|
# Purpose: This routine returns the specified key's keyrec from the
|
|
# specified zone from the specified keyrec file.
|
|
# This routine is big on specified things.
|
|
#
|
|
sub zonekeykr
|
|
{
|
|
my $rname = shift; # Name of rollrec record.
|
|
my $zname = shift; # Name of zone.
|
|
my $krname = shift; # Name of keyrec.
|
|
my $key = shift; # Key to check.
|
|
|
|
my $ret; # Return code.
|
|
my $krec; # Keyrec reference.
|
|
my %kh; # Keyrec hash.
|
|
|
|
#
|
|
# Close the open keyrec file.
|
|
#
|
|
keyrec_discard();
|
|
|
|
#
|
|
# Read the keyrec file.
|
|
#
|
|
$ret = keyrec_read($krname);
|
|
if($ret < 0)
|
|
{
|
|
if($ret == -3)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyname collision in keyrec \"$krname\"; marking rollrec as skipped");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to read keyrec \"$krname\"; marking rollrec as skipped");
|
|
}
|
|
skipnow($rname);
|
|
}
|
|
|
|
#
|
|
# Get the zone's keyrec and complain if it doesn't exist.
|
|
#
|
|
$krec = keyrec_fullrec($zname);
|
|
if(!defined($krec))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec file \"$krname\" does not contain a keyrec for zone \"$zname\"");
|
|
return(0);
|
|
}
|
|
%kh = %$krec;
|
|
|
|
#
|
|
# Get the key's keyrec and complain if it doesn't exist.
|
|
#
|
|
$key = $kh{$key};
|
|
$krec = keyrec_fullrec($key);
|
|
if(!defined($krec))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec file \"$krname\" does not contain a keyrec for key \"$key\"");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Return the keyrec reference.
|
|
#
|
|
return($krec);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: getkrval()
|
|
#
|
|
# Purpose: This routine returns the value of a specified field in a
|
|
# keyrec. Error checking is performed to ensure that the
|
|
# field exists in the keyrec and that it doesn't have an
|
|
# empty value.
|
|
#
|
|
sub getkrval
|
|
{
|
|
my $rname = shift; # Rollrec's name.
|
|
my $krr = shift; # Keyrec reference.
|
|
my $field = shift; # Keyrec's field.
|
|
|
|
my %kh = %$krr; # Keyrec hash.
|
|
my $val; # Field's value.
|
|
|
|
#
|
|
# Ensure that the requested field exists.
|
|
#
|
|
if(!defined($kh{$field}))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec has no entry for \"$field\"");
|
|
return("");
|
|
}
|
|
|
|
#
|
|
# Get the field from the keyrec.
|
|
#
|
|
$val = $kh{$field};
|
|
|
|
#
|
|
# Give an error if the value is empty.
|
|
#
|
|
if($val eq "")
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec has an empty entry for \"$field\"");
|
|
}
|
|
|
|
#
|
|
# Return the keyrec field.
|
|
#
|
|
return($val);
|
|
}
|
|
|
|
##############################################################################
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: optsandargs()
|
|
#
|
|
# Purpose: Parse our options and arguments.
|
|
#
|
|
sub optsandargs
|
|
{
|
|
my $argc = @ARGV; # Number of arguments.
|
|
my $opterrs = 0; # Count of option-related errors.
|
|
|
|
#
|
|
# Get the base values.
|
|
#
|
|
$dtconfig = getconffile();
|
|
%dtconf = parseconfig();
|
|
|
|
#
|
|
# Check our options.
|
|
#
|
|
opts_onerr(0);
|
|
|
|
#
|
|
# Get a bunch of option values.
|
|
#
|
|
$singlerun = $opts{$OPT_SINGLERUN};
|
|
$foreground = $opts{$OPT_FOREGROUND};
|
|
$alwayssign = $opts{$OPT_ALWAYSSIGN} || 0;
|
|
$pidfile = $opts{$OPT_PIDFILE};
|
|
$realm = $opts{$OPT_REALM};
|
|
$verbose = $opts{$OPT_VERBOSE};
|
|
$logfile = $opts{$OPT_LOGFILE} || $dtconf{$DT_LOGFILE};
|
|
$loglevel = $opts{$OPT_LOGLEVEL} || $dtconf{$DT_LOGLEVEL} || LOG_DEFAULT;
|
|
$logtz = $opts{$OPT_LOGTZ} || $dtconf{$DT_LOGTZ};
|
|
$sleeptime = $opts{$OPT_SLEEP} || $dtconf{$DT_SLEEP} || $DEFAULT_NAP;
|
|
$dtcf = $opts{$OPT_DTCONF} || getconffile();
|
|
$display = $opts{$OPT_DISPLAY} || 0;
|
|
$gzsargs = $opts{$OPT_ZSARGS} || '';
|
|
$username = $opts{$OPT_USERNAME} || $dtconf{$DT_USERNAME} || '';
|
|
$xqtdir = $opts{$OPT_DIR} || '.';
|
|
$zonesigner = $opts{$OPT_ZONESIGNER} || $dtconf{$OPT_ZONESIGNER} || '';
|
|
|
|
#
|
|
# Check for autosign presence or absence.
|
|
#
|
|
$autosign = defined($opts{'autosign'}) ?
|
|
$opts{$OPT_AUTOSIGN} :
|
|
(defined($dtconf{'autosign'}) ?
|
|
$dtconf{$OPT_AUTOSIGN} :
|
|
dnssec_tools_default("autosign"));
|
|
|
|
#
|
|
# Determine whether or not we'll load zones.
|
|
#
|
|
$zoneload = dnssec_tools_default($DT_LOADZONE);
|
|
$zoneload = $dtconf{$DT_LOADZONE} if(defined($dtconf{$DT_LOADZONE}));
|
|
$zoneload = 0 if(defined($opts{$OPT_NORELOAD}));
|
|
$zoneload = boolconvert($zoneload);
|
|
|
|
#
|
|
# Show the version number if requested
|
|
#
|
|
usage() if(defined($opts{$OPT_HELP}));
|
|
version() if(defined($opts{$OPT_VERSION}));
|
|
|
|
#
|
|
# Check for a rollrec file name.
|
|
#
|
|
$rollrecfile = $opts{$OPT_RRFILE} || rollrec_default();
|
|
$curdir = getcwd();
|
|
if($rollrecfile !~ /^\//)
|
|
{
|
|
$rollrecfile = "$curdir/$rollrecfile";
|
|
}
|
|
|
|
#
|
|
# Validate and switch to the given username -- if we can.
|
|
#
|
|
if($username ne '')
|
|
{
|
|
my $uid; # Uid we're switching to.
|
|
my $ret; # Return from setuid().
|
|
|
|
#
|
|
# If the username is really a uid, we'll convert it to a name.
|
|
#
|
|
if($username =~ /^[0-9]+$/)
|
|
{
|
|
$username = getpwuid($username);
|
|
}
|
|
|
|
#
|
|
# Convert the name to a uid.
|
|
#
|
|
$uid = getpwnam($username);
|
|
if($uid eq '')
|
|
{
|
|
print STDERR "rollerd: unknown user \"$username\"\n";
|
|
$opterrs++;
|
|
}
|
|
|
|
#
|
|
# Change the uid of the process.
|
|
#
|
|
$! = '';
|
|
$> = $uid;
|
|
# POSIX::setuid($uid);
|
|
$ret = $!;
|
|
if($ret ne '')
|
|
{
|
|
print STDERR "rollerd: unable to switch to user \"$username\" -- $ret\n";
|
|
$opterrs++;
|
|
}
|
|
}
|
|
|
|
#
|
|
# Build a global zonesigner argument string. We'll convert the
|
|
# internal equals signs to dashes or spaces, as appropriate.
|
|
#
|
|
if($gzsargs ne '')
|
|
{
|
|
my @args = split / /, $gzsargs; # Argument array.
|
|
|
|
#
|
|
# Put the arguments into the form zonesigner is expecting
|
|
# and join up the bits into a single argument string.
|
|
#
|
|
for(my $ind=0; $ind < @args; $ind++)
|
|
{
|
|
$args[$ind] =~ s/^=/-/;
|
|
$args[$ind] =~ s/=/ /g;
|
|
}
|
|
|
|
$gzsargs = join ' ', @args;
|
|
}
|
|
|
|
#
|
|
# Validate our execution directory.
|
|
#
|
|
if($xqtdir eq ".")
|
|
{
|
|
$xqtdir = `$PWD`;
|
|
chomp($xqtdir);
|
|
}
|
|
if(!defined($xqtdir))
|
|
{
|
|
print STDERR "rollerd: no execution directory defined\n";
|
|
$opterrs++;
|
|
}
|
|
if(! -e $xqtdir)
|
|
{
|
|
print STDERR "rollerd: execution directory \"$xqtdir\" does not exist\n";
|
|
$opterrs++;
|
|
}
|
|
if(! -d $xqtdir)
|
|
{
|
|
print STDERR "rollerd: execution directory \"$xqtdir\" is not a directory\n";
|
|
$opterrs++;
|
|
}
|
|
|
|
#
|
|
# If the user only wants the parameters, print 'em and exit.
|
|
#
|
|
if($opts{$OPT_PARAMS})
|
|
{
|
|
print "$ME parameters:\n";
|
|
print "\trollrec file \"$rollrecfile\"\n";
|
|
print "\tdirectory \"$xqtdir\"\n";
|
|
print "\tconfig file \"$dtconfig\"\n";
|
|
print "\tlogfile \"$logfile\"\n";
|
|
print "\tloglevel \"$loglevel\"\n";
|
|
print "\tlogtz \"$logtz\"\n";
|
|
print "\tautosign \"$autosign\"\n";
|
|
print "\tzone reload \"$zoneload\"\n";
|
|
print "\tsleeptime \"$sleeptime\"\n";
|
|
exit(0)
|
|
}
|
|
|
|
#
|
|
# Whine if the rollrec file doesn't exist.
|
|
#
|
|
if(!rrfokay($rollrecfile,""))
|
|
{
|
|
print STDERR "rollerd: rollrec file \"$rollrecfile\" does not exist\n\n";
|
|
print STDERR "rollerd: not stopping execution; waiting for a rollrec\n";
|
|
}
|
|
|
|
#
|
|
# Move into our execution directory.
|
|
#
|
|
chdir($xqtdir);
|
|
|
|
#
|
|
# Ensure a log file was given on the command line or in the config file.
|
|
#
|
|
if(!defined($logfile))
|
|
{
|
|
print STDERR "rollerd: no logfile specified on command line or in dnssec-tools.conf\n";
|
|
$opterrs++;
|
|
}
|
|
|
|
#
|
|
# Ensure the log file's directory actually exists.
|
|
#
|
|
if($logfile =~ /\//)
|
|
{
|
|
my @logbits; # Components of logfile's path.
|
|
my $logdir; # Logfile's directory.
|
|
|
|
#
|
|
# Get the log directory without the logfile.
|
|
#
|
|
@logbits = split /\//, $logfile;
|
|
pop @logbits;
|
|
$logdir = join '/', @logbits;
|
|
$logdir = '/' if($logdir eq '');
|
|
|
|
#
|
|
# Check for the existence of the log directory.
|
|
#
|
|
if(! -e $logdir)
|
|
{
|
|
print STDERR "rollerd: logfile's directory \"$logdir\" does not exist\n";
|
|
$opterrs++;
|
|
}
|
|
}
|
|
|
|
#
|
|
# If a pid file was specified, we'll pass it to the rollmgr module.
|
|
#
|
|
if(defined($pidfile) && ($pidfile ne ''))
|
|
{
|
|
rollmgr_set_idfile($pidfile);
|
|
}
|
|
|
|
#
|
|
# Set the logging level and file.
|
|
#
|
|
$loglevel_save = $loglevel;
|
|
rolllog_level($loglevel,1);
|
|
rolllog_file($logfile,1);
|
|
|
|
#
|
|
# Set the called-command options.
|
|
#
|
|
$rndcopts = $dtconf{$DT_RNDCOPTS};
|
|
|
|
#
|
|
# Set a dummy value to turn off KSK phase 5 mail, iff no recipient
|
|
# has been set.
|
|
#
|
|
if(!exists($dtconf{'admin-email'}))
|
|
{
|
|
$dtconf{'admin-email'} = 'nomail';
|
|
}
|
|
|
|
#
|
|
# Set the logging timezone.
|
|
#
|
|
if(rolllog_settz($logtz) eq '')
|
|
{
|
|
print STDERR "rollerd: invalid log timezone \"$logtz\"\n";
|
|
$opterrs++;
|
|
}
|
|
|
|
#
|
|
# Exit if there were any option-related errors.
|
|
#
|
|
exit(1) if($opterrs > 0);
|
|
|
|
#
|
|
# Start up our display program if -display was given.
|
|
#
|
|
displayer($display);
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: getprogs()
|
|
#
|
|
# Purpose: Get some program paths.
|
|
#
|
|
sub getprogs
|
|
{
|
|
#
|
|
# If we're running packed, we'll force the command paths to be taken
|
|
# from the packed versions.
|
|
#
|
|
# NOTE: In this version of packed rollerd, we're assuming
|
|
# that rollerd will not be interacting with named.
|
|
# Consequently, we won't be using rndc.
|
|
#
|
|
if($packed)
|
|
{
|
|
$rndc = '';
|
|
$keyarch = "$ENV{'PAR_TEMP'}/inc/keyarch";
|
|
$rrchk = "$ENV{'PAR_TEMP'}/inc/rollchk";
|
|
$zonesigner = "$ENV{'PAR_TEMP'}/inc/zonesigner";
|
|
|
|
delete($opts{'rndc'});
|
|
delete($opts{'rndcopts'});
|
|
$opts{'keyarch'} = "keyarch";
|
|
$opts{'rollchk'} = "rollchk";
|
|
$opts{'zonesigner'} = "zonesigner";
|
|
$opts{'keygen'} = "dnssec-keygen";
|
|
$opts{'zonesign'} = "dnssec-signzone";
|
|
$opts{'zonecheck'} = "named-checkzone";
|
|
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Get the paths to the external commands. If they aren't defined,
|
|
# use the default command names.
|
|
#
|
|
$keyarch = $dtconf{'keyarch'} || dnssec_tools_default("keyarch");
|
|
$rndc = $dtconf{'rndc'} || dnssec_tools_default("rndc");
|
|
$rndcopts = $dtconf{'rndcopts'} || dnssec_tools_default("rndcopts");
|
|
$rrchk = $dtconf{'rollrec_chk'} || dnssec_tools_default("rollrec_check");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: bootmsg()
|
|
#
|
|
# Purpose: Write a start-up message to the log.
|
|
#
|
|
sub bootmsg
|
|
{
|
|
my $bootflag = shift; # Boot flag.
|
|
|
|
if($bootflag)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"","$ME starting " . ("-" x 40));
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"","$ME changing logfiles " . ("-" x 31));
|
|
}
|
|
|
|
rolllog_log(LOG_ALWAYS,"","$ME parameters:");
|
|
rolllog_log(LOG_ALWAYS,""," rollrec file \"$rollrecfile\"");
|
|
rolllog_log(LOG_ALWAYS,""," realm \"$realm\"");
|
|
rolllog_log(LOG_ALWAYS,""," directory \"$xqtdir\"");
|
|
rolllog_log(LOG_ALWAYS,""," config file \"$dtconfig\"");
|
|
rolllog_log(LOG_ALWAYS,""," logfile \"$logfile\"");
|
|
rolllog_log(LOG_ALWAYS,""," loglevel \"$loglevel\"");
|
|
rolllog_log(LOG_ALWAYS,""," logtz \"$logtz\"");
|
|
rolllog_log(LOG_ALWAYS,""," always-sign \"". $alwayssign ."\"");
|
|
rolllog_log(LOG_ALWAYS,""," autosign \"$autosign\"");
|
|
rolllog_log(LOG_ALWAYS,""," single-run \"$singlerun\"");
|
|
rolllog_log(LOG_ALWAYS,""," zone reload \"$zoneload\"");
|
|
if($eventmaster == $EVT_FULLLIST)
|
|
{
|
|
rolllog_log(LOG_ALWAYS,""," sleeptime \"$sleeptime\"");
|
|
}
|
|
rolllog_log(LOG_ALWAYS,""," zone reload \"$zoneload\"");
|
|
rolllog_log(LOG_ALWAYS,""," event method \"$event_methods[$eventmaster]\"");
|
|
|
|
if($username ne '')
|
|
{
|
|
rolllog_log(LOG_ALWAYS,""," running as \"$username\"");
|
|
}
|
|
|
|
rolllog_log(LOG_ALWAYS,""," ");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: rrfchk()
|
|
#
|
|
# Purpose: This routine performs initial checking of the rollrec file.
|
|
# Several errors and problems are checked for in each rollrec
|
|
# marked as a a roll rollrec.
|
|
#
|
|
# Errors:
|
|
# The following errors are checked:
|
|
# - the zonefile exists
|
|
# - the keyrec file exists
|
|
#
|
|
# If any of these are violated, the rollrec's type will
|
|
# be changed to a skip rollrec. This prevents lots of
|
|
# unnecessary repeated log messages of an invalid rollrec.
|
|
#
|
|
# Problems:
|
|
# The following problems are checked:
|
|
# - no zonename field exists
|
|
#
|
|
# If any of these problems are found, they will be fixed.
|
|
#
|
|
sub rrfchk
|
|
{
|
|
my $rrf = shift; # Rollrec file.
|
|
my $modified = 0; # Modified flag.
|
|
|
|
#
|
|
# Return failure if the rollrec file is bad.
|
|
#
|
|
if(!rrfokay($rrf,""))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Get the current contents of the rollrec file.
|
|
#
|
|
rollrec_lock();
|
|
rollrec_read($rollrecfile);
|
|
|
|
#
|
|
# For each roll rollrec, check if its zonefile and keyrec file exist.
|
|
# If not, we'll change it to being a skip rollrec.
|
|
#
|
|
foreach my $rname (rollrec_names())
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
my $phase; # Rollrec's phase.
|
|
|
|
my $errs = 0; # Missing file errors.
|
|
my $prefix; # Rollrec's directory field.
|
|
my $keyrec; # Keyrec's filename.
|
|
my $zonefile; # Zone's filename.
|
|
|
|
#
|
|
# Get the rollrec for this name. If it doesn't have one,
|
|
# whinge and continue to the next.
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"no rollrec defined for zone");
|
|
next;
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Don't look at skip records.
|
|
#
|
|
next if($rr{'rollrec_type'} eq "skip");
|
|
|
|
#
|
|
# Check for a directory. We'll use rollerd's execution
|
|
# directory if one isn't defined.
|
|
#
|
|
$prefix = $xqtdir;
|
|
$prefix = $rr{'directory'} if(defined($rr{'directory'}));
|
|
|
|
#--------------------------------------------------
|
|
# Errors...
|
|
#
|
|
|
|
#
|
|
# Get our important files.
|
|
#
|
|
$zonefile = $rr{'zonefile'};
|
|
$zonefile = "$prefix/$zonefile" if($zonefile !~ /^\//);
|
|
$keyrec = $rr{'keyrec'};
|
|
$keyrec = "$prefix/$keyrec" if($keyrec !~ /^\//);
|
|
|
|
#
|
|
# Set the error flag if either the zonefile or the keyrec
|
|
# file don't exist.
|
|
#
|
|
if(! -e $zonefile)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec \"$rrr->{'keyrec'}\" does not exist; running initial zonesigner");
|
|
if(signer($rname, "initial", 0) != 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to do an initial signing of zone");
|
|
$errs++;
|
|
}
|
|
}
|
|
if(! -e $keyrec)
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"keyrec \"$keyrec\" does not exist");
|
|
$errs++;
|
|
}
|
|
|
|
#--------------------------------------------------
|
|
# Problems...
|
|
#
|
|
|
|
#
|
|
# Add a zonename field to the file and our rollrec hash
|
|
# if it isn't already in the hash.
|
|
#
|
|
if(!defined($rr{'zonename'}))
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"adding missing zonename field ($rname) to rollrec");
|
|
$rr{'zonename'} = $rname;
|
|
rollrec_setval($rname,'zonename',$rname);
|
|
}
|
|
|
|
#
|
|
# Get our important files.
|
|
#
|
|
$zonefile = $rr{'zonefile'};
|
|
$zonefile = "$prefix/$zonefile" if($zonefile !~ /^\//);
|
|
$keyrec = $rr{'keyrec'};
|
|
$keyrec = "$prefix/$keyrec" if($keyrec !~ /^\//);
|
|
|
|
#
|
|
# Skip this record if the zonefile and keyrec file exist.
|
|
#
|
|
next if(!$errs);
|
|
|
|
#
|
|
# If one of the files doesn't exist, then change the rollrec
|
|
# to a skip record. There's no reason we should bang our
|
|
# head against non-existent files.
|
|
#
|
|
rolllog_log(LOG_ERR,$rname,"changing roll record to a skip record");
|
|
rollrec_rectype($rname,"skip");
|
|
$modified++;
|
|
display("badzone \"$rname\" 0");
|
|
|
|
}
|
|
|
|
#
|
|
# Save the current rollrec file state.
|
|
#
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Return success.
|
|
#
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: rrfokay()
|
|
#
|
|
# Purpose: This routine checks to see if a rollrec file is okay.
|
|
#
|
|
# Returns:
|
|
# 0 - file is in error
|
|
# 1 - file is okay
|
|
#
|
|
sub rrfokay
|
|
{
|
|
my $rrf = shift; # Rollrec file to check.
|
|
my $mp = shift; # Message prefix.
|
|
my $ret = 1; # Return code.
|
|
my $err; # Error message.
|
|
|
|
#
|
|
# Check that the file exists and is non-zero in length. If one of
|
|
# those conditions fails, we'll set an error message and return code.
|
|
#
|
|
if(! -e $rrf)
|
|
{
|
|
$err = "rollrec file \"$rrf\" does not exist";
|
|
$ret = 0;
|
|
}
|
|
elsif(-z $rrf)
|
|
{
|
|
$err = "rollrec file \"$rrf\" is zero length";
|
|
$ret = 0;
|
|
}
|
|
|
|
#
|
|
# If we found an error, we'll (maybe) give an error message and
|
|
# bump our error count.
|
|
#
|
|
if($ret == 0)
|
|
{
|
|
if($rrferrors == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,$mp,$err);
|
|
$rrferrors = $MAXRRFERRS;
|
|
}
|
|
else
|
|
{
|
|
$rrferrors--;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Reset our error count and return success.
|
|
#
|
|
$rrferrors = 0;
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: clearzoneerr()
|
|
#
|
|
# Purpose: Reset a zone's error count.
|
|
#
|
|
sub clearzoneerr
|
|
{
|
|
my $rname = shift; # Name of rollrec rec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
#
|
|
# Get the rollrec's hash.
|
|
#
|
|
%rr = %$rrr;
|
|
|
|
if($rr{'curerrors'} != 0)
|
|
{
|
|
rollrec_setval($rname,'curerrors',0);
|
|
|
|
rolllog_log(LOG_TMI,$rname,"resetting the error count");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zoneerr()
|
|
#
|
|
# Purpose: Increment a zone's error count. If the count of maximum
|
|
# consecutive errors is exceeded, the zone is changed to
|
|
# be a roll zone.
|
|
#
|
|
# zoneerr() DOES NOT log the error! This must be done by
|
|
# its caller. It was decided to do it this way since the
|
|
# caller is likely to have more information about the problem
|
|
# and can provide a more cogent explanation without passing a
|
|
# big pile of extra parameters.
|
|
#
|
|
sub zoneerr
|
|
{
|
|
my $rname = shift; # Name of rollrec rec.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
|
|
my %rr; # Rollrec hash.
|
|
my $curerrs = 0; # Zone's current error count.
|
|
my $maxerrs; # Zone's maximum error count.
|
|
|
|
#
|
|
# Get the rollrec's hash.
|
|
#
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Get the zone's maximum error count.
|
|
#
|
|
$maxerrs = $rr{'maxerrors'};
|
|
$maxerrs = $dtconf{'zone_errors'} if(! exists($rr{'maxerrors'}));
|
|
|
|
#
|
|
# If there's a maximum error count set for this zone, we'll increase
|
|
# the count and see if we need to stop worrying about this zone.
|
|
#
|
|
if($maxerrs > 0)
|
|
{
|
|
#
|
|
# Increment the zone's maximum error count.
|
|
#
|
|
$curerrs = $rr{'curerrors'} + 1;
|
|
|
|
#
|
|
# Save the new value.
|
|
#
|
|
$rr{'curerrors'} = $curerrs;
|
|
rollrec_setval($rname,'curerrors',$curerrs);
|
|
rollrec_write();
|
|
|
|
#
|
|
# If we've exceeded the maximum error count, change the zone
|
|
# to a skip zone.
|
|
#
|
|
if($curerrs > $maxerrs)
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"too many errors, zone will now be skipped");
|
|
skipnow($rname);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: sleeper()
|
|
#
|
|
# Purpose: Sleep for a specific amount of time. This will take into
|
|
# account interrupts we've taken from rollctl.
|
|
# We may be overridden by a rollctl command.
|
|
#
|
|
sub sleeper
|
|
{
|
|
my $nap; # Time left to sleep.
|
|
|
|
return if($sleep_override);
|
|
|
|
rolllog_log(LOG_TMI,"","sleeping for $sleeptime seconds");
|
|
|
|
$sleepcnt = 0;
|
|
|
|
while($sleepcnt < $sleeptime)
|
|
{
|
|
$nap = $sleeptime - $sleepcnt;
|
|
|
|
$sleepcnt += sleep($nap);
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: sleeper_awake()
|
|
#
|
|
# Purpose: This routine forces rollerd to run its queue. It does this
|
|
# by setting the elapsed-sleep-time equal to the time-to-sleep.
|
|
#
|
|
sub sleeper_awake
|
|
{
|
|
$sleepcnt = $sleeptime;
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: sleeper_override()
|
|
#
|
|
# Purpose: This routine sets the sleep-override flag.
|
|
#
|
|
sub sleeper_override
|
|
{
|
|
$sleep_override = 1;
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: commander()
|
|
#
|
|
# Purpose: Get any commands sent to rollerd's command socket.
|
|
#
|
|
sub commander
|
|
{
|
|
my $cmd; # Client's command.
|
|
my $data; # Command's data.
|
|
my $gstr = ROLLMGR_GROUP; # Group command indicator.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"checking commands");
|
|
|
|
#
|
|
# Read and handle all the commands we've been sent.
|
|
#
|
|
while(42)
|
|
{
|
|
#
|
|
# Get the command, return if there wasn't one.
|
|
#
|
|
($cmd,$data) = rollmgr_getcmd(5);
|
|
return if(!defined($cmd));
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"cmd - \"$cmd\"");
|
|
rolllog_log(LOG_TMI,'<command>',"data - \"$data\"") if($data ne "");
|
|
|
|
#
|
|
# Deal with the command as zone-related or as a group command.
|
|
#
|
|
if($cmd =~ /^$gstr/)
|
|
{
|
|
|
|
$cmd =~ s/^$gstr//;
|
|
groupcmd($cmd,$data);
|
|
}
|
|
else
|
|
{
|
|
if(singlecmd($cmd,$data) == 1)
|
|
{
|
|
last;
|
|
}
|
|
}
|
|
|
|
rollmgr_closechan();
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: groupcmd()
|
|
#
|
|
# Purpose: Execute a command for each zone in a zone group.
|
|
#
|
|
sub groupcmd
|
|
{
|
|
my $cmd = shift; # Client's command.
|
|
my $data = shift; # Command's data.
|
|
my %zgcmds; # Zone group commands.
|
|
my @zonegroup; # Zones in the named group.
|
|
|
|
#
|
|
# Get the list of recognized zone-group commands. If the requested
|
|
# command isn't allowed for zone groups, we'll quietly run it as
|
|
# a regular command.
|
|
#
|
|
%zgcmds = rollrec_zonegroup_cmds();
|
|
if(! defined($zgcmds{$cmd}))
|
|
{
|
|
singlecmd($cmd,$data);
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Run the named command on each zone in the specified zone group.
|
|
#
|
|
@zonegroup = rollrec_zonegroup($data);
|
|
foreach my $zn (@zonegroup)
|
|
{
|
|
singlecmd($cmd,$zn);
|
|
}
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: singlecmd()
|
|
#
|
|
# Purpose: Execute a single command.
|
|
#
|
|
sub singlecmd
|
|
{
|
|
my $cmd = shift; # Client's command.
|
|
my $data = shift; # Command's data.
|
|
|
|
if($cmd eq ROLLCMD_DISPLAY())
|
|
{
|
|
cmd_display($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_DSPUB())
|
|
{
|
|
cmd_dspub($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_DSPUBALL())
|
|
{
|
|
cmd_dspuball();
|
|
}
|
|
elsif($cmd eq ROLLCMD_LOGFILE())
|
|
{
|
|
cmd_logfile($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_LOGLEVEL())
|
|
{
|
|
cmd_loglevel($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_LOGMSG())
|
|
{
|
|
cmd_logmsg($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_LOGTZ())
|
|
{
|
|
cmd_logtz($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_MERGERRFS())
|
|
{
|
|
cmd_mergerrfs($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_PHASEMSG())
|
|
{
|
|
cmd_phasemsg($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLALL())
|
|
{
|
|
cmd_rollall();
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLALLKSKS())
|
|
{
|
|
cmd_rollallksks();
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLALLZSKS())
|
|
{
|
|
cmd_rollallzsks();
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLREC())
|
|
{
|
|
return(1) if(cmd_rollrec($data));
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLKSK())
|
|
{
|
|
cmd_rollnow($data,'KSK');
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLZONE())
|
|
{
|
|
cmd_rollzone($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_ROLLZSK())
|
|
{
|
|
cmd_rollnow($data,'ZSK');
|
|
}
|
|
elsif($cmd eq ROLLCMD_RUNQUEUE())
|
|
{
|
|
cmd_runqueue($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_QUEUELIST())
|
|
{
|
|
cmd_queuelist($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_QUEUESTATUS())
|
|
{
|
|
cmd_queuestatus($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_SHUTDOWN())
|
|
{
|
|
cmd_shutdown($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_SIGNZONE())
|
|
{
|
|
cmd_signzone($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_SIGNZONES())
|
|
{
|
|
cmd_signzones($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_SKIPALL())
|
|
{
|
|
cmd_skipall();
|
|
}
|
|
elsif($cmd eq ROLLCMD_SKIPZONE())
|
|
{
|
|
cmd_skipzone($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_SLEEPTIME())
|
|
{
|
|
cmd_sleeptime($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_SPLITRRF())
|
|
{
|
|
cmd_splitrrf($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_STATUS())
|
|
{
|
|
cmd_status($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_ZONEGROUP())
|
|
{
|
|
cmd_zonegroup($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_ZONELOG())
|
|
{
|
|
cmd_zonelog($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_ZONESTATUS())
|
|
{
|
|
cmd_zonestatus($data);
|
|
}
|
|
elsif($cmd eq ROLLCMD_ZSARGS())
|
|
{
|
|
cmd_zsargs($data);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"invalid command - \"$cmd\"");
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_display()
|
|
#
|
|
# Purpose: Start or stop the rollerd display program.
|
|
#
|
|
sub cmd_display
|
|
{
|
|
my $onflag = shift; # Display-on flag.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"display command received; status flag - \"$onflag\"");
|
|
|
|
#
|
|
# Do nothing if the we're already doing what the user wants.
|
|
#
|
|
if($display == $onflag)
|
|
{
|
|
if($display == 1)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"graphical display is already on");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"graphical display is already off");
|
|
}
|
|
rollmgr_sendresp(ROLLCMD_RC_DISPLAY,"display status already as requested");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Change the logfile and give an appropriate set of log messages.
|
|
#
|
|
if($onflag)
|
|
{
|
|
displayer(1);
|
|
rolllog_log(LOG_INFO,'<command>',"rollerd display started");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd display started");
|
|
}
|
|
else
|
|
{
|
|
displayer(0);
|
|
rolllog_log(LOG_INFO,'<command>',"rollerd display stopped");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd display stopped");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_dspub()
|
|
#
|
|
# Purpose: Move a zone from KSK rollover phase 5 to phase 6.
|
|
#
|
|
sub cmd_dspub
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
my $curdir; # Current directory.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"dspub command received; zone - \"$zone\"");
|
|
|
|
#
|
|
# Move the zone to KSK phase 6. This might also do some directory
|
|
# swapping, so we'll save the current directory as well.
|
|
#
|
|
$curdir = cwd();
|
|
dspubber('dspub',$zone);
|
|
|
|
#
|
|
# Return to the previous directory.
|
|
#
|
|
chdir($curdir);
|
|
|
|
sleeper_override();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_dspuball()
|
|
#
|
|
# Purpose: Move all zones that are currently in KSK rollover phase 5
|
|
# to phase 6.
|
|
#
|
|
sub cmd_dspuball
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
my $phase; # Zone's phase.
|
|
my $curdir; # Current directory.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"dspuball command received");
|
|
|
|
#
|
|
# This might do some directory swapping, so we'll save the
|
|
# current directory.
|
|
#
|
|
$curdir = cwd();
|
|
|
|
#
|
|
# Move all the zones in KSK phase 5 over to KSK phase 6.
|
|
#
|
|
foreach my $zone (sort(rollrec_names()))
|
|
{
|
|
dspubber('dspuball',$zone);
|
|
}
|
|
|
|
#
|
|
# Return to the previous directory.
|
|
#
|
|
chdir($curdir);
|
|
|
|
sleeper_override();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_logfile()
|
|
#
|
|
# Purpose: Set the logfile to the user's specified file. Nothing is done
|
|
# if the user requests the same log file as is currently in use.
|
|
#
|
|
sub cmd_logfile
|
|
{
|
|
my $newlog = shift; # New log file.
|
|
my $oldlog; # Old log file.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"logfile command received; new log file - \"$newlog\"");
|
|
|
|
#
|
|
# Do nothing if the new logfile is the same as the current logfile.
|
|
#
|
|
$oldlog = rolllog_file();
|
|
if($oldlog eq $newlog)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"new logfile ($newlog) same as the old logfile");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Change the logfile and give an appropriate set of log messages.
|
|
#
|
|
rolllog_log(LOG_INFO,'<command>',"logfile changed from $oldlog to $newlog");
|
|
rolllog_log(LOG_INFO,'<command>',"closing logfile $oldlog");
|
|
$oldlog = rolllog_file($newlog,0);
|
|
if($oldlog ne "")
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"logfile changed from $oldlog to $newlog");
|
|
bootmsg(0);
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"logfile changed to \"$newlog\"");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"unable to change logfile to $newlog");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADFILE,"unable to change logfile to \"$newlog\"");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_loglevel()
|
|
#
|
|
# Purpose: Set the logging level to the user's specified level.
|
|
#
|
|
sub cmd_loglevel
|
|
{
|
|
my $newlvl = shift; # New level.
|
|
my $oldlvl; # Old level.
|
|
|
|
my $oldstr; # Old level's string.
|
|
my $newstr; # New level's string.
|
|
|
|
$newstr = rolllog_str($newlvl);
|
|
rolllog_log(LOG_TMI,'<command>',"loglevel command received; new logging level - \"$newstr\"");
|
|
|
|
$oldlvl = rolllog_level($newlvl,0);
|
|
if($oldlvl >= 0)
|
|
{
|
|
$oldstr = rolllog_str($oldlvl);
|
|
|
|
rolllog_log(LOG_INFO,'<command>',"loglevel changed from $oldstr to $newstr");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"loglevel changed from \"$oldstr\" to \"$newstr\"");
|
|
|
|
$loglevel = $newlvl;
|
|
$loglevel_save = $newlvl;
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"unable to change loglevel to $newlvl");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADLEVEL,"invalid loglevel \"$newlvl\"");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_logmsg()
|
|
#
|
|
# Purpose: Write a log message to the log file.
|
|
#
|
|
# The way we check for a valid log level is only good in a
|
|
# single-threaded environment.
|
|
#
|
|
sub cmd_logmsg
|
|
{
|
|
my $logstr = shift; # Log level and message.
|
|
|
|
my $loglvl; # Logging level.
|
|
my $logmsg; # Log message.
|
|
|
|
my $oldlvl; # Real logging level.
|
|
my $goodlvl; # Real version of new level.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"logmsg command received");
|
|
|
|
#
|
|
# Dig the log level and message out of our argument.
|
|
#
|
|
$logstr =~ /^\((\w+)\)(.*)$/;
|
|
$loglvl = $1;
|
|
$logmsg = $2;
|
|
|
|
#
|
|
# Check the validity of the caller's log level. If it's invalid,
|
|
# rolllog_level() will return a failure and we'll return.
|
|
#
|
|
$oldlvl = rolllog_level($loglvl,0);
|
|
if($oldlvl < 0)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"invalid loglevel ($loglvl) specified for logmsg command");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADLEVEL,"invalid loglevel \"$loglvl\"");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Reset the logging level and write the caller's message.
|
|
#
|
|
$goodlvl = rolllog_level($oldlvl,0);
|
|
rolllog_log($goodlvl,"<user msg>",$logmsg);
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"message written to log");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_logtz()
|
|
#
|
|
# Purpose: Set the timezone for log messages' timestamps.
|
|
#
|
|
sub cmd_logtz
|
|
{
|
|
my $newtz = shift; # New logging timezone.
|
|
|
|
my $oldtz; # Current logging timezone.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"logtz command received");
|
|
|
|
#
|
|
# Set the new timezone for log messages. If it's invalid, we'll
|
|
# return a failure and then return.
|
|
#
|
|
$oldtz = rolllog_settz($newtz);
|
|
if($oldtz eq '')
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"invalid timezone ($newtz) specified for logtz command");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADTZ,"invalid logtz \"$newtz\"");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Let the user know all's well.
|
|
#
|
|
rolllog_log(LOG_INFO,"<command>","logging timezone set to $newtz");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"new logging timezone set");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_mergerrfs()
|
|
#
|
|
# Purpose: Merge a set of rollrec files into the current rollrec and
|
|
# start rollover for those zones.
|
|
#
|
|
sub cmd_mergerrfs
|
|
{
|
|
my $rrfs = shift; # New rollrec files.
|
|
my @rrfs; # List of rollrec files.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"mergerrfs command received");
|
|
|
|
#
|
|
# Divide the string of rollrec files into a list.
|
|
#
|
|
@rrfs = split ':', $rrfs;
|
|
|
|
#
|
|
# Merge the new rollrecs with the current one.
|
|
#
|
|
if(rollrec_merge($rollrecfile,@rrfs) < 0)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"unable to merge rollrecs ($rrfs) with current rollrec file");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADFILE,"unable to merge rollrecs \"$rrfs\"");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Let the user know all's well.
|
|
#
|
|
rolllog_log(LOG_INFO,"<command>","zone files merged");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"zonefiles merged");
|
|
display("readrrf \"dummy\" 0");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_phasemsg()
|
|
#
|
|
# Purpose: Set the phase-message length for log messages.
|
|
#
|
|
sub cmd_phasemsg
|
|
{
|
|
my $pmsg = shift; # Phase-message length.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"phasemsg command received");
|
|
|
|
#
|
|
# Set our phase-message length.
|
|
#
|
|
if(rollmgr_phasemsg($pmsg) == 1)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,'phase-message length set');
|
|
return;
|
|
}
|
|
|
|
#
|
|
# If it fails, return the failure to the caller.
|
|
#
|
|
rolllog_log(LOG_INFO,'<command>',"invalid phase-message ($pmsg)");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADLEVEL,"invalid phase-message length \"$pmsg\"");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollall()
|
|
#
|
|
# Purpose: This command resumes rollover for all suspended zones in the
|
|
# rollrec file. The zones' rollrec records are marked as being
|
|
# "roll" records, which will cause rollerd to start working on
|
|
# them. This change is reflected in the rollrec file. rollnow()
|
|
# is called for each suspended zone, in order to resume rollover.
|
|
# We'll also keep track of the suspended zones we were and weren't
|
|
# able to stop and report them to the caller.
|
|
#
|
|
sub cmd_rollall
|
|
{
|
|
my $good = ""; # Resumed zones.
|
|
my $bad = ""; # Unresumed zones.
|
|
|
|
my $cnt = 0; # Total count.
|
|
my $gcnt = 0; # Resumed count.
|
|
my $bcnt = 0; # Unresumed count.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"rollall command received");
|
|
|
|
#
|
|
# Each suspended zone in the rollrec file will be returned to the
|
|
# rollover process. We'll keep track of the suspended zones that were
|
|
# resumed and those that weren't in order to provide an appropriate
|
|
# response message.
|
|
#
|
|
foreach my $zone (rollrec_names())
|
|
{
|
|
$cnt++;
|
|
|
|
#
|
|
# If the resume worked, increment the good count and add
|
|
# the domain name to the list of good zones. If it didn't
|
|
# work, do the same for the bad count and bad-zone list.
|
|
#
|
|
if(rollnow($zone,'restart',1) == 1)
|
|
{
|
|
$gcnt++;
|
|
$good .= "$zone ";
|
|
}
|
|
else
|
|
{
|
|
$bcnt++;
|
|
$bad .= "$zone ";
|
|
}
|
|
}
|
|
|
|
#
|
|
# Get rid of trailing whitespace.
|
|
#
|
|
chomp $good;
|
|
chomp $bad;
|
|
|
|
#
|
|
# Send a response message to the caller.
|
|
#
|
|
if($gcnt == $cnt)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$good);
|
|
}
|
|
else
|
|
{
|
|
my $resp; # Response message.
|
|
|
|
$resp = "unable to resume roll process for zones: $bad\n";
|
|
|
|
#
|
|
# If there were any zones that were resumed, we'll add them
|
|
# to the message as well.
|
|
#
|
|
if($gcnt > 0)
|
|
{
|
|
$resp .= "zones now resumed: $good\n";
|
|
}
|
|
|
|
chomp $resp;
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,$resp);
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollallksks()
|
|
#
|
|
# Purpose: This command moves all the zones in the rollrec file into
|
|
# immediate KSK rollover.
|
|
#
|
|
sub cmd_rollallksks
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"rollallksks command received");
|
|
rollemall('KSK');
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollallzsks()
|
|
#
|
|
# Purpose: This command moves all the zones in the rollrec file into
|
|
# immediate ZSK rollover.
|
|
#
|
|
sub cmd_rollallzsks
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"rollallzsks command received");
|
|
rollemall('ZSK');
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollrec()
|
|
#
|
|
# Purpose: This routine changes the rollrec file being used by rollerd.
|
|
# If this command is successful, then the communications channel
|
|
# will be closed.
|
|
#
|
|
sub cmd_rollrec
|
|
{
|
|
my $rrf = shift; # New rollrec file.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"rollrec command received; rollrec file - \"$rrf\"");
|
|
|
|
if(rrfokay($rrf,'<command>'))
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"------------------------------------------------");
|
|
rolllog_log(LOG_INFO,'<command>',"setting rollrec file from \"$rollrecfile\" to \"$rrf\"");
|
|
$rollrecfile = $rrf;
|
|
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd now using rollrec file $rrf");
|
|
display("rollrec \"$rrf\" 0");
|
|
|
|
#
|
|
# Close the channel and return success.
|
|
#
|
|
rollmgr_closechan();
|
|
return(1);
|
|
}
|
|
|
|
rolllog_log(LOG_ERR,'<command>',"invalid rollrec file \"$rrf\"");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADROLLREC,"invalid rollrec file \"$rrf\"");
|
|
return(0);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollnow()
|
|
#
|
|
# Purpose: This command moves a zone into immediate KSK or ZSK rollover.
|
|
# It calls rollnow() to move the zone into immediate rollover.
|
|
#
|
|
sub cmd_rollnow
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
my $rolltype = shift; # Type of rollover.
|
|
|
|
my $rtlc; # LC rollover type.
|
|
my $rollret; # Retcode from rolling.
|
|
my $rrr; # Rollrec reference.
|
|
my %rr; # Rollrec hash.
|
|
|
|
$rtlc = lc($rolltype);
|
|
rolllog_log(LOG_TMI,'<command>',"roll" . $rtlc . " command received; zone - \"$zone\"");
|
|
|
|
#
|
|
# Get the zone's rollrec.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"no rollrec defined for zone $zone");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"$zone not in rollrec file $rollrecfile");
|
|
return(0);
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Don't proceed if this zone is in the middle of KSK rollover.
|
|
#
|
|
if($rr{'kskphase'} > 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"in KSK rollover (phase $rr{'kskphase'}); not attempting ZSK rollover");
|
|
rollmgr_sendresp(ROLLCMD_RC_KSKROLL,"$zone is already engaged in a KSK rollover");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Don't proceed if this zone is in the middle of ZSK rollover.
|
|
#
|
|
if($rr{'zskphase'} > 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"in ZSK rollover (phase $rr{'zskphase'}); not attempting ZSK rollover");
|
|
rollmgr_sendresp(ROLLCMD_RC_ZSKROLL,"$zone is already engaged in a ZSK rollover");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Do the rollover and send an appropriate response.
|
|
#
|
|
$rollret = rollnow($zone,$rolltype,1);
|
|
if($rollret == 1)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"$zone $rolltype rollover started");
|
|
}
|
|
elsif($rollret == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"$zone not in rollrec file $rollrecfile");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"$zone not in rollrec file $rollrecfile");
|
|
}
|
|
elsif($rollret == -1)
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"$zone has bad values in rollrec file $rollrecfile");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONEDATA,"$zone has bad values in rollrec file $rollrecfile");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollzone()
|
|
#
|
|
# Purpose: This command restores a skipped zone to rollover processing.
|
|
#
|
|
sub cmd_rollzone
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"rollzone command received; zone - \"$zone\"");
|
|
|
|
if(rollnow($zone,'restart',1) == 1)
|
|
{
|
|
display("startroll \"$zone\" 0");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"$zone rollover started");
|
|
}
|
|
else
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"$zone not in rollrec file $rollrecfile");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_rollzsk()
|
|
#
|
|
# Purpose: This command moves a zone into immediate rollover. It calls
|
|
# rollnow() to move the zone into immediate ZSK rollover.
|
|
#
|
|
sub cmd_rollzsk
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
my $rollret; # Retcode from rolling.
|
|
|
|
my $rrr; # Rollrec reference.
|
|
my %rr; # Rollrec hash.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"rollzsk command received; zone - \"$zone\"");
|
|
|
|
#
|
|
# Get the zone's rollrec.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"no rollrec defined for zone $zone");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"$zone not in rollrec file $rollrecfile");
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Don't proceed if this zone is in the middle of KSK rollover.
|
|
#
|
|
if($rr{'kskphase'} > 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"in KSK rollover (phase $rr{'kskphase'}); not attempting ZSK rollover");
|
|
rollmgr_sendresp(ROLLCMD_RC_KSKROLL,"$zone is already engaged in a KSK rollover");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Don't proceed if this zone is in the middle of ZSK rollover.
|
|
#
|
|
if($rr{'zskphase'} > 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"in ZSK rollover (phase $rr{'zskphase'}); not attempting ZSK rollover");
|
|
rollmgr_sendresp(ROLLCMD_RC_ZSKROLL,"$zone is already engaged in a ZSK rollover");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Do the ZSK rollover and send an appropriate response.
|
|
#
|
|
$rollret = rollnow($zone,'ZSK',1);
|
|
if($rollret == 1)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"$zone ZSK rollover started");
|
|
}
|
|
elsif($rollret == 0)
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"$zone not in rollrec file $rollrecfile");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"$zone not in rollrec file $rollrecfile");
|
|
}
|
|
elsif($rollret == -1)
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"$zone has bad values in rollrec file $rollrecfile");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONEDATA,"$zone has bad values in rollrec file $rollrecfile");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_runqueue()
|
|
#
|
|
# Purpose: This command forces rollerd to run its queue. It does this
|
|
# by setting the elapsed-sleep-time equal to the time-to-sleep.
|
|
# Upon return, sleeper() will see that sleepers must awake,
|
|
# and rollerd will go run its rollrec queue.
|
|
#
|
|
sub cmd_runqueue
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"runqueue command received");
|
|
|
|
rolllog_log(LOG_INFO,'<command>',"checking rollrec queue");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd checking rollrec queue");
|
|
sleeper_awake();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_queuelist()
|
|
#
|
|
# Purpose: This command returns the soon queue. The zones and times
|
|
# until their next events are returned.
|
|
#
|
|
sub cmd_queuelist
|
|
{
|
|
my $zncnt; # Number of zones we'll report.
|
|
my $outstr = ''; # Response to caller.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"queuelist command received");
|
|
|
|
#
|
|
# Ensure that this command makes sense to respond to.
|
|
#
|
|
if($eventmaster != $EVT_QUEUE_SOON)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_BADEVENT,"rollerd not running the queue-soon event handler");
|
|
return;
|
|
}
|
|
|
|
$zncnt = @queue_sooners - $queue_firstsoon;
|
|
|
|
$outstr = "Soon-Queue List ($zncnt zones)\n";
|
|
|
|
for(my $ind = $queue_firstsoon; $ind < @queue_sooners; $ind++)
|
|
{
|
|
my $zn = $queue_sooners[$ind];
|
|
$outstr .= " $zn " . timetrans($queue_eventtimes{$zn}) . " until next event\n";
|
|
}
|
|
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$outstr);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_queuestatus()
|
|
#
|
|
# Purpose: This command returns the status of the queuing system.
|
|
#
|
|
sub cmd_queuestatus
|
|
{
|
|
my $allcnt; # Count of all zones we're managing.
|
|
my $sooncnt; # Count of zones that will roll soon.
|
|
my $scantime; # Time until next full scan.
|
|
my $nexttime; # Time until next rollover event.
|
|
my $fprdtime; # Time between full scans.
|
|
my $soonend; # End of current soon period.
|
|
|
|
my $nextzone; # Zone with next rollover event.
|
|
my $outstr; # Response to caller.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"queuestatus command received");
|
|
|
|
#
|
|
# Ensure that this command makes sense to respond to.
|
|
#
|
|
if($eventmaster != $EVT_QUEUE_SOON)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_BADEVENT,"rollerd not running the queue-soon event handler");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Get the data we'll need...
|
|
#
|
|
$allcnt = @queue_allzones;
|
|
$sooncnt = @queue_sooners;
|
|
$soonend = time + $queue_eventtimes{$QUEUE_RUNSCAN};
|
|
$soonend = localtime($soonend);
|
|
|
|
$scantime = timetrans($queue_eventtimes{$QUEUE_RUNSCAN});
|
|
$nextzone = $queue_sooners[$queue_firstsoon];
|
|
|
|
$nexttime = $queue_eventtimes{$nextzone};
|
|
if($nexttime <= 0)
|
|
{
|
|
$nexttime = "happening now";
|
|
}
|
|
else
|
|
{
|
|
$nexttime = "in " . timetrans($nexttime);
|
|
}
|
|
|
|
$fprdtime = timetrans($QUEUE_SOONLIMIT);
|
|
|
|
#
|
|
# ... and build a response.
|
|
#
|
|
$outstr = "$event_methods[$eventmaster] queuing method\n";
|
|
$outstr = "$fprdtime between FULL scans\n";
|
|
$outstr .= "$allcnt zones managed\n";
|
|
$outstr .= "$sooncnt zones with SOON rollover events\n";
|
|
$outstr .= "\n";
|
|
$outstr .= "next FULL zone scan in $scantime\n";
|
|
$outstr .= "end of current SOON scan period $soonend (local)\n";
|
|
$outstr .= "next rollover event $nexttime ($nextzone)\n";
|
|
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$outstr);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_shutdown()
|
|
#
|
|
# Purpose: This command forces rollerd to shut down.
|
|
#
|
|
sub cmd_shutdown
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"shutdown command received");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd shutting down");
|
|
|
|
halt_handler();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_signzone()
|
|
#
|
|
# Purpose: This command causes a zone signing, without any key creation
|
|
# or rolling.
|
|
#
|
|
sub cmd_signzone
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
|
|
my $rrr; # Rollrec reference.
|
|
my $krr; # Keyrec reference.
|
|
my $phasestr; # Phase argument.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"signzone command received");
|
|
|
|
#
|
|
# Get the current phase values for the zone.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
|
|
#
|
|
# Get the phase string we'll use for signing.
|
|
#
|
|
$phasestr = phaseargs($rrr->{'kskphase'},$rrr->{'zskphase'});
|
|
|
|
#
|
|
# Check value and file existence, etc.
|
|
#
|
|
$krr = opts_zonekr($rrr->{'keyrec'},$zone);
|
|
|
|
#
|
|
# Sign the zone.
|
|
#
|
|
rolllog_log(LOG_PHASE,'<command>',"mid-phase, user-initiated signing of $zone");
|
|
if(signer($zone,$phasestr,$krr) == 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"rollerd signed zone $zone");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd signed zone $zone");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"unable to sign zone $zone");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"unable to sign zone $zone");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_signzones()
|
|
#
|
|
# Purpose: This command causes all unskipped zones to be signed, without
|
|
# any key creation or rolling.
|
|
#
|
|
sub cmd_signzones
|
|
{
|
|
my $skipflag = shift; # Command's data.
|
|
|
|
my $rrr; # Rollrec reference.
|
|
my $krr; # Keyrec reference.
|
|
my $phasestr; # Phase argument.
|
|
my @errzones = (); # Unsigned zones.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"signzones command received; data - \"$skipflag\"");
|
|
|
|
#
|
|
# Convert the textual zone-skip flag into an easy-to-use boolean.
|
|
#
|
|
$skipflag = ($skipflag eq 'active') ? 1 : 0;
|
|
|
|
#
|
|
# Sign each active zone in the rollrec file. Any skipped zones will
|
|
# not be signed.
|
|
#
|
|
foreach my $zone (rollrec_names())
|
|
{
|
|
#
|
|
# Get the current rollrec for this zone and (maybe) skip
|
|
# any skipped zones.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
next if($skipflag && ($rrr->{'rollrec_type'} eq 'skip'));
|
|
|
|
#
|
|
# Get the phase string we'll use for signing.
|
|
#
|
|
$phasestr = phaseargs($rrr->{'kskphase'},$rrr->{'zskphase'});
|
|
|
|
#
|
|
# Check value and file existence, etc.
|
|
#
|
|
$krr = opts_zonekr($rrr->{'keyrec'},$zone);
|
|
|
|
#
|
|
# Sign the zone.
|
|
#
|
|
rolllog_log(LOG_PHASE,'<command>',"mid-phase, user-initiated signing of $zone");
|
|
if(signer($zone,$phasestr,$krr) != 0)
|
|
{
|
|
push @errzones, $zone;
|
|
}
|
|
}
|
|
|
|
#
|
|
# If we signed all the zones, we'll send a success message.
|
|
# If we couldn't sign any zones, we'll return the list of bad
|
|
# zones to the caller.
|
|
#
|
|
if(@errzones == 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"rollerd signed all zones");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"rollerd signed all zones");
|
|
}
|
|
else
|
|
{
|
|
my $errstr = join(', ', @errzones);
|
|
rolllog_log(LOG_ERR,'<command>',"unable to sign all zones: $errstr");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"unable to sign all zones: $errstr");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_skipall()
|
|
#
|
|
# Purpose: This command stops rollover for all the zones in the rollrec
|
|
# file. The zones' rollrec records are marked as being "skip"
|
|
# records, which will cause rollerd to ignore them. This change
|
|
# is reflected in the rollrec file. skipnow() is called for each
|
|
# zone, in order to stop rollover. We'll also keep track of the
|
|
# zones we were and weren't able to stop and report them to the
|
|
# caller.
|
|
#
|
|
sub cmd_skipall
|
|
{
|
|
my $good = ""; # Skipped zones.
|
|
my $bad = ""; # Unskipped zones.
|
|
|
|
my $cnt = 0; # Total count.
|
|
my $gcnt = 0; # Skipped count.
|
|
my $bcnt = 0; # Unskipped count.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"skipall command received");
|
|
|
|
#
|
|
# Each zone in the rollrec file will be removed from the rollover
|
|
# process. We'll keep track of the zones that were skipped and those
|
|
# that weren't in order to provide an appropriate response message.
|
|
#
|
|
foreach my $zone (rollrec_names())
|
|
{
|
|
$cnt++;
|
|
|
|
#
|
|
# If the skip worked, increment the good count and add
|
|
# the domain name to the list of good zones. If it didn't
|
|
# work, do the same for the bad count and bad-zone list.
|
|
#
|
|
if(skipnow($zone) == 1)
|
|
{
|
|
$gcnt++;
|
|
$good .= "$zone ";
|
|
}
|
|
else
|
|
{
|
|
$bcnt++;
|
|
$bad .= "$zone ";
|
|
}
|
|
}
|
|
|
|
#
|
|
# We still don't need no steenkin' trailing whitespace.
|
|
#
|
|
chomp $good;
|
|
chomp $bad;
|
|
|
|
#
|
|
# Send a response message to the caller.
|
|
#
|
|
if($gcnt == $cnt)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$good);
|
|
}
|
|
else
|
|
{
|
|
my $resp; # Response message.
|
|
|
|
$resp = "zones not in rollrec file $rollrecfile: $bad\n";
|
|
|
|
#
|
|
# If there were any zones that were removed from rollover,
|
|
# we'll add them to the message as well.
|
|
#
|
|
if($gcnt > 0)
|
|
{
|
|
$resp .= "zones now skipped: $good\n";
|
|
}
|
|
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,$resp);
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_skipzone()
|
|
#
|
|
# Purpose: This command removes a zone from rollover processing.
|
|
#
|
|
sub cmd_skipzone
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"skipzone command received; zone - \"$zone\"");
|
|
|
|
if(skipnow($zone) == 1)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"$zone rollover stopped");
|
|
}
|
|
else
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"$zone not in rollrec file $rollrecfile");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_sleeptime()
|
|
#
|
|
# Purpose: This command sets rollerd's time-to-sleep to the user's
|
|
# specified value. This must be above a "constant" minimum
|
|
# sleep time in order to prevent malicious users from making
|
|
# rollerd continually run its queue without any resting.
|
|
#
|
|
# It might be good to also enforce a maximum sleep time.
|
|
#
|
|
sub cmd_sleeptime
|
|
{
|
|
my $newnap = shift; # New sleep time.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"sleeptime command received; new sleep time - \"$newnap\"");
|
|
|
|
if($newnap < $MIN_SLEEP)
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"sleep-time must be >= $MIN_SLEEP");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADSLEEP,"invalid sleep-time \"$newnap\"");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"setting sleep-time to $newnap");
|
|
$sleeptime = $newnap;
|
|
$sleepcnt = $newnap;
|
|
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"sleep-time set to $newnap");
|
|
display("sleeptime \"all\" $sleeptime");
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_splitrrf()
|
|
#
|
|
# Purpose: Split the current rollrec file in two. The specified rollrec
|
|
# entries are moved into a new file.
|
|
#
|
|
sub cmd_splitrrf
|
|
{
|
|
my $args = shift; # New rollrec file and rollrecs.
|
|
my $newrrf; # New rollrec file.
|
|
my @rrfs; # Rollrec entries to move.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"splitrrf command received");
|
|
|
|
#
|
|
# Divide the string of arguments into a list.
|
|
#
|
|
@rrfs = split ':', $args;
|
|
|
|
#
|
|
# Get the new rollrec file name from the front of the list.
|
|
#
|
|
$newrrf = shift @rrfs;
|
|
|
|
#
|
|
# Lock the file so others can't use it. We'll also read the file
|
|
# to make sure we have the current version.
|
|
#
|
|
rollrec_lock();
|
|
rollrec_close();
|
|
rollrec_read($rollrecfile);
|
|
|
|
#
|
|
# Split the rollrecs into the new file.
|
|
#
|
|
if(rollrec_split($newrrf,@rrfs) < 0)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"unable to split rollrec from current rollrec file to \"$newrrf\"");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADFILE,"unable to split current rollrec to \"$newrrf\"");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Unlock the file so others can use it.
|
|
#
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Let the user know all's well.
|
|
#
|
|
rolllog_log(LOG_INFO,"<command>","current rollrec split to \"$newrrf\"");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"zonefiles split");
|
|
display("readrrf \"dummy\" 0");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_status()
|
|
#
|
|
# Purpose: Give a caller some rollerd settings.
|
|
#
|
|
sub cmd_status
|
|
{
|
|
my $data = shift; # Command's data.
|
|
|
|
my $lfile; # Log file.
|
|
my $lvl; # Logging level.
|
|
my $lvlstr; # Logging level string.
|
|
my $tz; # Logging timezone.
|
|
my $outbuf = ""; # Response buffer.
|
|
|
|
#
|
|
# Get the info to report.
|
|
#
|
|
$lfile = rolllog_file();
|
|
$lvl = rolllog_level();
|
|
$lvlstr = rolllog_str($lvl);
|
|
$tz = rolllog_gettz();
|
|
|
|
#
|
|
# Build status report.
|
|
#
|
|
$outbuf = "boot-time: $boottime\n";
|
|
$outbuf .= "realm: $realm\n";
|
|
$outbuf .= "directory: $curdir\n";
|
|
$outbuf .= "rollrec file: $rollrecfile\n";
|
|
$outbuf .= "config file: $dtconfig\n";
|
|
$outbuf .= "logfile: $lfile\n";
|
|
$outbuf .= "loglevel: $lvl\n";
|
|
$outbuf .= "logtz: $tz\n";
|
|
$outbuf .= "always-sign: $alwayssign\n";
|
|
$outbuf .= "autosign: $autosign\n";
|
|
$outbuf .= "zone reload: $zoneload\n";
|
|
|
|
if($eventmaster == $EVT_FULLLIST)
|
|
{
|
|
$outbuf .= "sleeptime: $sleeptime\n";
|
|
}
|
|
|
|
$outbuf .= "event method: $event_methods[$eventmaster]\n";
|
|
|
|
if($username ne '')
|
|
{
|
|
$outbuf .= "running as: $username\n";
|
|
}
|
|
|
|
$outbuf .= "\n";
|
|
$outbuf .= "$VERS\n";
|
|
$outbuf .= "$DTVERS\n";
|
|
|
|
#
|
|
# Send the status report to the caller.
|
|
#
|
|
rolllog_log(LOG_TMI,'<command>',"status command received");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$outbuf);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_zonegroup()
|
|
#
|
|
# Purpose: Give a caller info about the zone groups or the list of zone
|
|
# in a specified group.
|
|
#
|
|
sub cmd_zonegroup
|
|
{
|
|
my $data = shift; # Command's data.
|
|
my $outbuf = ''; # Response buffer.
|
|
my $errbuf = ''; # Error buffer.
|
|
|
|
#
|
|
# Get the info to report.
|
|
#
|
|
if($data eq '')
|
|
{
|
|
my %zgroups; # Zone groups.
|
|
|
|
#
|
|
# Get the list of zone groups.
|
|
#
|
|
%zgroups = rollrec_zonegroups();
|
|
|
|
#
|
|
# Build the list of zone groups and count of zones in
|
|
# that group.
|
|
#
|
|
foreach my $zg (sort(keys(%zgroups)))
|
|
{
|
|
$outbuf .= "$zg:\t$zgroups{$zg} zones\n";
|
|
}
|
|
|
|
#
|
|
# Mark an error if there are no zone groups.
|
|
#
|
|
if($outbuf eq '')
|
|
{
|
|
$errbuf = "no zone groups defined";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
my @zgroup; # Zone group.
|
|
|
|
@zgroup = rollrec_zonegroup($data);
|
|
|
|
#
|
|
# Buid the list of zones in this group. If there are no
|
|
# zones in this group, this isn't really a zone group.
|
|
#
|
|
if(@zgroup > 0)
|
|
{
|
|
$outbuf = join("\n",@zgroup);
|
|
}
|
|
else
|
|
{
|
|
$errbuf = "invalid zone group \"$data\"";
|
|
}
|
|
|
|
}
|
|
|
|
#
|
|
# Send the status report to the caller.
|
|
#
|
|
chomp $outbuf;
|
|
if($outbuf ne '')
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"zonegroup($data) command received");
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$outbuf);
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',$errbuf);
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONEGROUP,$errbuf);
|
|
}
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_zonelog()
|
|
#
|
|
# Purpose: Set the logging level for a zone.
|
|
#
|
|
sub cmd_zonelog
|
|
{
|
|
my $data = shift; # Command's data.
|
|
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $zone; # User's zone.
|
|
my $llev; # User's logging level.
|
|
my $llevstr; # String of log level.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"zonelog command received");
|
|
|
|
#
|
|
# Get the zone and logging level from the command data.
|
|
#
|
|
$data =~ /^(.*):(.*)$/;
|
|
$zone = $1;
|
|
$llev = $2;
|
|
|
|
#
|
|
# Convert the user's logging level to its numeric form.
|
|
#
|
|
$llev = rolllog_num($llev);
|
|
|
|
#
|
|
# Ensure that the logging level is valid.
|
|
#
|
|
if($llev == -1)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_BADLEVEL,"invalid logging level \"$llev\"");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"invalid logging level \"$llev\" specified for zone \"$zone\"\n");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Read the rollrec file. Complain and return if we couldn't.
|
|
#
|
|
rollrec_lock();
|
|
if(rollrec_read($rollrecfile) <= 0)
|
|
{
|
|
rollrec_unlock();
|
|
rollmgr_sendresp(ROLLCMD_RC_RRFOPEN,"unable to open rollrec file $rollrecfile");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"unable to open rollrec file $rollrecfile\n");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Get the specified zone's roll record.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
if(!defined($rrr))
|
|
{
|
|
rollrec_unlock();
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"invalid zone \"$zone\" specified");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"invalid zone \"$zone\" specified for zonelog command\n");
|
|
return;
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Set the zone's logging level.
|
|
#
|
|
rollrec_setval($zone,"loglevel",$llev);
|
|
|
|
#
|
|
# Write and unlock the rollrec file.
|
|
#
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Report the command and return.
|
|
#
|
|
$llevstr = rolllog_str($llev);
|
|
rolllog_log(LOG_INFO,'<command>',"set the logging level for zone $zone to $llevstr");
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_zonestatus()
|
|
#
|
|
# Purpose: Return zone status to the control program.
|
|
#
|
|
sub cmd_zonestatus
|
|
{
|
|
my $data = shift; # Command's data.
|
|
|
|
my $cnt = 0; # Zone count.
|
|
my $outbuf = ""; # Zone status line.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"zonestatus command received");
|
|
|
|
rollrec_lock();
|
|
|
|
#
|
|
# Read the rollrec file. If we couldn't, complain and return.
|
|
#
|
|
if(rollrec_read($rollrecfile) <= 0)
|
|
{
|
|
rollrec_unlock();
|
|
rollmgr_sendresp(ROLLCMD_RC_RRFOPEN,"unable to open rollrec file $rollrecfile");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"unable to open rollrec file $rollrecfile\n");
|
|
return;
|
|
}
|
|
|
|
# rolllog_log(LOG_ALWAYS,'<command>',"zone status:");
|
|
|
|
#
|
|
# Add the status of each zone in the rollrec file to our output buffer.
|
|
#
|
|
foreach my $rname (rollrec_names())
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $phase; # Rollrec's phase.
|
|
my $pstr; # Phase string.
|
|
|
|
my $rtype; # Rollrec's record type.
|
|
|
|
#
|
|
# Get the rollrec for this name.
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
next if(!defined($rrr));
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Get the data we're interested in.
|
|
#
|
|
if($rr{'kskphase'} > 0)
|
|
{
|
|
$phase = 'KSK ' . $rr{'kskphase'};
|
|
$pstr = rollmgr_get_phase('KSK', $rr{'kskphase'});
|
|
}
|
|
else
|
|
{
|
|
$phase = 'ZSK ' . $rr{'zskphase'};
|
|
$pstr = rollmgr_get_phase('ZSK', $rr{'zskphase'});
|
|
}
|
|
|
|
$phase = "$phase: $pstr" if($pstr ne '');
|
|
$rtype = $rr{'rollrec_type'};
|
|
$phase = '-' if($rtype eq 'skip');
|
|
|
|
#
|
|
# Add the data to the output buffer and bump our zone count.
|
|
#
|
|
$outbuf .= "$rname/$rr{'zonename'}\t$rtype\t$phase\n";
|
|
# rolllog_log(LOG_ALWAYS,'<command>',"\t$rname/$rr{'zonename'}\t$rtype\t$phase\n");
|
|
$cnt++;
|
|
}
|
|
|
|
#
|
|
# Send a response to the control program.
|
|
#
|
|
if($cnt == 0)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_NOZONES,"no zones defined in $rollrecfile");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"no zones defined in $rollrecfile\n");
|
|
}
|
|
else
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$outbuf);
|
|
}
|
|
|
|
rollrec_unlock();
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cmd_zsargs()
|
|
#
|
|
# Purpose: Set zone-specific arguments.
|
|
#
|
|
sub cmd_zsargs
|
|
{
|
|
my $data = shift; # Command's data.
|
|
|
|
my @args = (); # Command's split args.
|
|
my @zsargs = (); # Zonesigner's args.
|
|
my @zones = (); # Zones to modify.
|
|
my @badzones = (); # Invalid zones.
|
|
|
|
my $zsargstr = ''; # Final argument string.
|
|
my $clear = 0; # Flag for zapping argstr.
|
|
|
|
rolllog_log(LOG_TMI,'<command>',"zsargs command received");
|
|
|
|
#
|
|
# Split the argument into its molecules.
|
|
#
|
|
@args = split /,/, $data;
|
|
|
|
#
|
|
# Divide up the arguments between zones and zonesigner arguments.
|
|
# Zonesigner arguments have equal signs; zones don't.
|
|
#
|
|
foreach my $arg (@args)
|
|
{
|
|
if($arg =~ /\=/)
|
|
{
|
|
push @zsargs, $arg;
|
|
}
|
|
else
|
|
{
|
|
push @zones, $arg;
|
|
}
|
|
}
|
|
|
|
#
|
|
# Ensure we've got both zones and zonesigner arguments.
|
|
#
|
|
if(@zsargs == 0)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_NOARGS,"no arguments given to zsargs command");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"no arguments given to zsargs command\n");
|
|
return;
|
|
}
|
|
if(@zones == 0)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_NOZONES,"no zones given to zsargs command");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"no zones given to zsargs command\n");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Put the arguments into the form zonesigner is expecting and join
|
|
# up the bits into a single argument string.
|
|
#
|
|
for(my $ind=0; $ind < @zsargs; $ind++)
|
|
{
|
|
$zsargs[$ind] =~ s/^=/-/;
|
|
$zsargs[$ind] =~ s/=/ /;
|
|
}
|
|
|
|
$zsargstr = join ' ', @zsargs;
|
|
|
|
#
|
|
# Set the "clear argument" flag if the fake "clear" option is given.
|
|
#
|
|
$clear = 1 if($zsargstr =~ /-clear/);
|
|
|
|
#
|
|
# Read the rollrec file. If we couldn't read it, complain and return.
|
|
#
|
|
rollrec_lock();
|
|
if(rollrec_read($rollrecfile) <= 0)
|
|
{
|
|
rollrec_unlock();
|
|
rollmgr_sendresp(ROLLCMD_RC_RRFOPEN,"unable to open rollrec file $rollrecfile");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"unable to open rollrec file $rollrecfile\n");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Ensure the specified zones are in the rollrec file.
|
|
#
|
|
foreach my $rname (@zones)
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
|
|
$rrr = rollrec_fullrec($rname);
|
|
if(!defined($rrr))
|
|
{
|
|
push @badzones, $rname;
|
|
}
|
|
}
|
|
|
|
#
|
|
# Return an error if any zones were specified that aren't in the
|
|
# rollrec file,
|
|
#
|
|
if(@badzones > 0)
|
|
{
|
|
rollrec_unlock();
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"non-existent zones specified - @badzones");
|
|
rolllog_log(LOG_ALWAYS,'<command>',"non-existent zones specified - @badzones\n");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Set (or clear) the new zsargs field in the rollrec entries for the
|
|
# given zones.
|
|
#
|
|
foreach my $zone (@zones)
|
|
{
|
|
if($clear)
|
|
{
|
|
rollrec_delfield($zone,"zsargs");
|
|
rolllog_log(LOG_INFO,'<command>',"clearing zsargs from zone $zone to $zsargstr");
|
|
}
|
|
else
|
|
{
|
|
rollrec_setval($zone,"zsargs",$zsargstr);
|
|
rolllog_log(LOG_INFO,'<command>',"setting zsargs for zone $zone to $zsargstr");
|
|
}
|
|
}
|
|
|
|
#
|
|
# Tidy up the rollrec file and tell the user we're done.
|
|
#
|
|
rollrec_write();
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"zsargs command completed\n");
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: dspubber()
|
|
#
|
|
# Purpose: Move the specified zone to KSK rollover phase 6, but only if
|
|
# it's in KSK phase 5.
|
|
#
|
|
# If the zone isn't in KSK phase 5, then the zone's phase will
|
|
# be left alone. If the call came from cmd_dspub(), then error
|
|
# messages will be generated; if it came from cmd_dspuball(),
|
|
# then no error messages will be given.
|
|
#
|
|
# WARNING: dspubber() calls chrrdir() to move into a zone's
|
|
# directory. After doing its job, it does *not*
|
|
# return to the previous directory and it expects the
|
|
# caller to do so. This is to minimize the number of
|
|
# chdir()'s done when the caller is cmd_dspuball().
|
|
#
|
|
sub dspubber
|
|
{
|
|
my $cmd = shift; # Command we were called from.
|
|
my $rname = shift; # Command's data.
|
|
my $rrr; # Reference to rollrec.
|
|
my $phase; # Zone's phase.
|
|
|
|
#
|
|
# Get the zone's rollrec.
|
|
#
|
|
$rrr = rollrec_fullrec($rname);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"no rollrec defined for zone \"$rname\"");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,"\"$rname\" not in rollrec file $rollrecfile");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Ensure we're in the proper KSK-rollover phase to do this command.
|
|
#
|
|
$phase = rollrec_recval($rname,'kskphase');
|
|
if($phase != 5)
|
|
{
|
|
if($cmd eq 'dspub')
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"\"$rname\" not in KSK-rollover phase 5");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONEDATA,"\"$rname\" not in KSK-rollover phase 5");
|
|
}
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Move to the zone's directory.
|
|
#
|
|
if(chrrdir($rname,$rrr->{'directory'},"$cmd:") == 0)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"unable to move to \"$rname\" directory \"$rrr->{'directory'}\"");
|
|
rollmgr_sendresp(ROLLCMD_RC_BADROLLREC,"unable to move to $rname's directory \"$rrr->{'directory'}\"");
|
|
return;
|
|
}
|
|
|
|
#
|
|
# Change the zone's rollover phase to phase 6.
|
|
#
|
|
nextphase($rname,$rrr,6,'KSK');
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,"zone \"$rname\" now in KSK rollover phase 6");
|
|
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: rollemall()
|
|
#
|
|
# Purpose: This command moves all the zones in the rollrec file into
|
|
# immediate rollover. The type of rollover is specified by the
|
|
# caller. rollnow() is called for each zone, in order to start
|
|
# rollover. We'll also keep track of the zones we were and
|
|
# weren't able to put in rollover, and report them to the caller.
|
|
#
|
|
sub rollemall
|
|
{
|
|
my $rt = shift; # Rollover type.
|
|
|
|
my $good = ''; # Rolled zones.
|
|
my $bad = ''; # Unrolled zones.
|
|
|
|
my $cnt = 0; # Total count.
|
|
my $gcnt = 0; # Rolled count.
|
|
my $bcnt = 0; # Unrolled count.
|
|
|
|
#
|
|
# Each zone in the rollrec file will be put in the rollover process,
|
|
# starting after that initial wait period. We'll keep track of the
|
|
# zones that were rolled and those that weren't in order to provide
|
|
# an appropriate response message.
|
|
#
|
|
foreach my $zone (rollrec_names())
|
|
{
|
|
$cnt++;
|
|
|
|
#
|
|
# If the rollover worked, increment the good count and add
|
|
# the domain name to the list of good zones. If it didn't
|
|
# work, do the same for the bad count and bad-zone list.
|
|
#
|
|
if(rollnow($zone,$rt,0) == 1)
|
|
{
|
|
$gcnt++;
|
|
$good .= "$zone ";
|
|
}
|
|
else
|
|
{
|
|
$bcnt++;
|
|
$bad .= "$zone ";
|
|
}
|
|
}
|
|
|
|
#
|
|
# We don't need no steenkin' trailing whitespace.
|
|
#
|
|
chomp $good;
|
|
chomp $bad;
|
|
|
|
#
|
|
# Send a response message to the caller.
|
|
#
|
|
if($gcnt == $cnt)
|
|
{
|
|
rollmgr_sendresp(ROLLCMD_RC_OKAY,$good);
|
|
}
|
|
else
|
|
{
|
|
my $resp; # Response message.
|
|
|
|
$resp = "zones not in rollrec file $rollrecfile: $bad\n";
|
|
|
|
#
|
|
# If there were any zones that were put into rollover,
|
|
# we'll add them to the message as well.
|
|
#
|
|
if($gcnt > 0)
|
|
{
|
|
$resp .= "zones now in $rt rollover: $good\n";
|
|
}
|
|
|
|
rollmgr_sendresp(ROLLCMD_RC_BADZONE,$resp);
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: rollnow()
|
|
#
|
|
# Purpose: This command moves a zone into immediate rollover for the
|
|
# specified key type. It doesn't sit in the initial waiting
|
|
# period, but starts right off in rollover phase 2.
|
|
#
|
|
sub rollnow
|
|
{
|
|
my $zone = shift; # Zone to roll.
|
|
my $rolltype = shift; # Type of roll to start.
|
|
my $force = shift; # Force-rollover flag.
|
|
my $rollstr; # String of roll type.
|
|
|
|
#
|
|
# Re-read the rollrec file and change the record's type. We'll
|
|
# also move the zone to phase 2 of rollover.
|
|
#
|
|
rollrec_lock();
|
|
if(rollrec_read($rollrecfile) > 0)
|
|
{
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
my $rndir; # Directory on entry.
|
|
my $dir; # Zone's directory.
|
|
my $krf; # Zone's keyrec.
|
|
my $znf; # Zone's zonefile.
|
|
|
|
#
|
|
# Get the rollrec for the zone.
|
|
#
|
|
$rndir = getcwd();
|
|
|
|
#
|
|
# Get the rollrec for the zone.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"no rollrec defined for zone $zone");
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
return(0);
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# If the caller isn't demanding a rollover, we'll make sure
|
|
# the zone isn't already rolling.
|
|
#
|
|
if(! $force)
|
|
{
|
|
if($rr{'kskphase'} > 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"in KSK rollover (phase $rr{'kskphase'}); not attempting rollover");
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
return(0);
|
|
}
|
|
|
|
if($rr{'zskphase'} > 0)
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"in ZSK rollover (phase $rr{'zskphase'}); not attempting rollover");
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
|
|
#
|
|
# Do a few very simple validation checks.
|
|
#
|
|
$dir = '.';
|
|
$krf = $rr{'keyrec'};
|
|
$znf = $rr{'zonefile'};
|
|
if(exists($rr{'directory'}))
|
|
{
|
|
$dir = $rr{'directory'};
|
|
$krf = "$dir/$krf" if($krf !~ /^\//);
|
|
$znf = "$dir/$znf" if($znf !~ /^\//);
|
|
}
|
|
if((! -e $dir) || (! -e $krf) || (! -e $znf))
|
|
{
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
return(-1);
|
|
}
|
|
|
|
#
|
|
# Go into zone's directory if we aren't there already.
|
|
#
|
|
chdir($dir) if($dir ne $rndir);
|
|
|
|
#
|
|
# A skip record is changed to a regular rollrec.
|
|
#
|
|
if($rr{'rollrec_type'} eq 'skip')
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"$zone skip rollrec changed to a roll rollrec");
|
|
rollrec_rectype($zone,"roll");
|
|
$rrr = rollrec_fullrec($zone);
|
|
}
|
|
|
|
#
|
|
# Change the zone's phase to rollover phase 1 (starting).
|
|
#
|
|
# WJH: This used to be set to phase 2 to bypass
|
|
# the initial rollover waiting period and get right to the
|
|
# nitty gritty of doing a rollover. But I changed it back
|
|
# to phase one, which is especially important for frequent
|
|
# or sudden rollovers.
|
|
#
|
|
if($rolltype eq 'KSK')
|
|
{
|
|
nextphase($zone,$rrr,1,'KSK');
|
|
$rollstr = 'KSK rollover';
|
|
}
|
|
elsif($rolltype eq 'ZSK')
|
|
{
|
|
nextphase($zone,$rrr,1,'ZSK');
|
|
$rollstr = 'ZSK rollover';
|
|
}
|
|
elsif($rolltype eq 'restart')
|
|
{
|
|
# Do nothing, just move from skip to roll.
|
|
$rollstr = 'restart';
|
|
}
|
|
|
|
#
|
|
# Write the rollrec file.
|
|
#
|
|
rollrec_write();
|
|
|
|
#
|
|
# Return to entry directory if we're in zone's directory.
|
|
#
|
|
chdir($rndir) if($dir ne $rndir);
|
|
}
|
|
|
|
#
|
|
# Unlock the rollrec file.
|
|
#
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Set the sleep values such that the queue will be run.
|
|
#
|
|
sleeper_awake();
|
|
rolllog_log(LOG_INFO,'<command>',"forcing a $rollstr for zone $zone");
|
|
|
|
#
|
|
# This is probably not the right thing to do.
|
|
#
|
|
display("startroll \"$zone\" 0");
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: skipnow()
|
|
#
|
|
# Purpose: This command prevents a zone from rolling over. It is marked
|
|
# as a skip record in the rollrec file.
|
|
#
|
|
sub skipnow
|
|
{
|
|
my $zone = shift; # Command's data.
|
|
my $rrr; # Reference to rollrec.
|
|
my %rr; # Rollrec hash.
|
|
|
|
#
|
|
# Get the rollrec for the zone.
|
|
#
|
|
$rrr = rollrec_fullrec($zone);
|
|
if(!defined($rrr))
|
|
{
|
|
rolllog_log(LOG_ERR,'<command>',"no rollrec defined for zone $zone");
|
|
return(0);
|
|
}
|
|
%rr = %$rrr;
|
|
|
|
#
|
|
# Re-read the rollrec file and change the record's type. We'll
|
|
# also move the zone to phase 0 of both KSK and ZSK rollover.
|
|
#
|
|
rollrec_lock();
|
|
if(rollrec_read($rollrecfile) > 0)
|
|
{
|
|
#
|
|
# A roll record is changed to a skip rollrec and its
|
|
# phases set to 0.
|
|
#
|
|
if($rr{'rollrec_type'} eq "roll")
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"$zone roll rollrec changed to a skip rollrec");
|
|
|
|
rollrec_rectype($zone,"skip");
|
|
display("stoproll \"$zone\" 0");
|
|
}
|
|
|
|
#
|
|
# Set the zone's KSK and ZSK rollover phases to 0.
|
|
#
|
|
# rollrec_setval($zone,"kskphase",0);
|
|
# rollrec_setval($zone,"zskphase",0);
|
|
|
|
#
|
|
# Write the rollrec file.
|
|
#
|
|
rollrec_write();
|
|
|
|
}
|
|
|
|
#
|
|
# Unlock the rollrec file.
|
|
#
|
|
rollrec_close();
|
|
rollrec_unlock();
|
|
|
|
#
|
|
# Log the event and return success.
|
|
#
|
|
rolllog_log(LOG_INFO,'<command>',"forcing a skiproll for zone $zone");
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: controllers()
|
|
#
|
|
# Purpose: Initialize handlers for our externally provided commands.
|
|
#
|
|
sub controllers
|
|
{
|
|
my $onflag = shift; # Handler on/off flag.
|
|
|
|
#
|
|
# Set the signal handlers that will manage incoming commands.
|
|
#
|
|
if($onflag)
|
|
{
|
|
if($queued_int)
|
|
{
|
|
$queued_int = 0;
|
|
halt_handler();
|
|
}
|
|
|
|
if($queued_hup)
|
|
{
|
|
$queued_hup = 0;
|
|
intcmd_handler();
|
|
}
|
|
|
|
$SIG{'HUP'} = \&intcmd_handler;
|
|
$SIG{'INT'} = \&halt_handler;
|
|
}
|
|
else
|
|
{
|
|
$SIG{'HUP'} = \&queue_hup_handler;
|
|
$SIG{'INT'} = \&queue_int_handler;
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: loadzone()
|
|
#
|
|
# Purpose: Initiates zone-reload, but obeys the $zoneload flag.
|
|
#
|
|
sub loadzone
|
|
{
|
|
my $rndc = shift; # Zone-loading program.
|
|
my $rname = shift; # Rollrec name of zone.
|
|
my $rrr = shift; # Reference to rollrec.
|
|
my $phase = shift; # Zone's current phase.
|
|
|
|
my %rr = %$rrr; # Rollrec hash.
|
|
my $zonename; # Zone to reload.
|
|
my $useopts; # Options for rndc.
|
|
my $ret; # Return code.
|
|
|
|
#
|
|
# Get the rollrec for this name. If it doesn't have one,
|
|
# whinge and continue to the next.
|
|
# (This should never happen, but...)
|
|
#
|
|
$zonename = $rr{'zonename'};
|
|
$useopts = defined($rr{'rndc_opts'}) ? $rr{'rndc_opts'} : $rndcopts;
|
|
|
|
#
|
|
# If the user doesn't want to reload the zone, we'll pretend we have.
|
|
#
|
|
if(!$zoneload)
|
|
{
|
|
rolllog_log(LOG_INFO,$rname,"not reloading zone for $phase\n");
|
|
return(0);
|
|
}
|
|
|
|
#
|
|
# Reload the zone for real.
|
|
#
|
|
rolllog_log(LOG_INFO,$rname,"reloading zone for $phase\n");
|
|
$ret = rollmgr_loadzone($rndc,$useopts,$zonename);
|
|
return($ret);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: zonemodified()
|
|
#
|
|
# Purpose: Checks if a zone file has been modified more recently than
|
|
# its signed version. If so, then the zone file will be
|
|
# re-signed -- without any keys created or other rollover
|
|
# actions taken.
|
|
#
|
|
# The "more recent" check is performed by comparing the date
|
|
# of last modification for the two zone files. If the unsigned
|
|
# zonefile's date is greater than the signed zonefile's date,
|
|
# then it is assumed the unsigned zonefile was modified.
|
|
#
|
|
# This only works if the signed and unsigned zonefiles are
|
|
# in different files.
|
|
#
|
|
sub zonemodified
|
|
{
|
|
my $rrr = shift; # Rollrec reference.
|
|
my $rname = shift; # Zone under consideration.
|
|
|
|
my $krf = $rrr->{'keyrec'}; # Zone's keyrec file.
|
|
my $zfs; # Signed zonefile.
|
|
my $zfu; # Unsigned zonefile.
|
|
my @zfst; # Zonefile stat info.
|
|
my $zfstime; # Update time of signed zone.
|
|
my $zfutime; # Update time of unsigned zone.
|
|
|
|
#
|
|
# Go no further here if we aren't autosigning.
|
|
#
|
|
return if(! $autosign);
|
|
|
|
#
|
|
# Make sure we've got the correct keyrec file.
|
|
#
|
|
keyrec_read($krf);
|
|
|
|
#
|
|
# Get the name of the zonefile. If the name is null, we'll read
|
|
# the keyrec and get the name again.
|
|
#
|
|
if(($zfu = keyrec_recval($rrr->{'zonename'},'zonefile')) eq '')
|
|
{
|
|
keyrec_read($krf);
|
|
$zfu = keyrec_recval($rrr->{'zonename'},'zonefile');
|
|
}
|
|
|
|
#
|
|
# Now we'll get the name of the signed zonefile.
|
|
#
|
|
$zfs = keyrec_recval($rrr->{'zonename'},'signedzone');
|
|
if($zfs ne $rrr->{'zonefile'})
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"rollrec and keyrec disagree about name of signed zone file");
|
|
rolllog_log(LOG_ERR,$rname," rollrec's signed zone file - $rrr->{'zonefile'}");
|
|
rolllog_log(LOG_ERR,$rname," keyrec's signed zone file - $zfs");
|
|
}
|
|
|
|
#
|
|
# Get the last modification time of the signed zonefile.
|
|
#
|
|
@zfst = stat($zfs);
|
|
$zfstime = @zfst[9];
|
|
|
|
#
|
|
# Get the last modification time of the unsigned zonefile.
|
|
#
|
|
@zfst = stat($zfu);
|
|
$zfutime = @zfst[9];
|
|
|
|
#
|
|
# Check the last modification times, and sign the zonefile if it's
|
|
# been changed more recently than the unsigned zonefile.
|
|
#
|
|
if($zfutime > $zfstime)
|
|
{
|
|
my $krr; # Ref to zone's keyrec.
|
|
my $pstr; # Phase string.
|
|
|
|
rolllog_log(LOG_PHASE,$rname,"zonefile modified; re-signing");
|
|
|
|
#
|
|
# Get the current phase (in string form) and a ref
|
|
# to the keyrec.
|
|
#
|
|
$pstr = phaseargs($rrr->{'kskphase'},$rrr->{'zskphase'});
|
|
$krr = keyrec_fullrec($rname);
|
|
|
|
#
|
|
# Sign -- just sign -- the zone.
|
|
#
|
|
if(signer($rname,$pstr,$krr) == 0)
|
|
{
|
|
rolllog_log(LOG_TMI,$rname,"rollerd signed zone");
|
|
}
|
|
else
|
|
{
|
|
rolllog_log(LOG_ERR,$rname,"unable to sign zone");
|
|
}
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: phaseargs()
|
|
#
|
|
# Purpose: Translates the KSK phase or ZSK phase into the appropriate
|
|
# arguments for a sign-only zonesigner call.
|
|
#
|
|
sub phaseargs
|
|
{
|
|
my $kpval = shift; # KSK phase value.
|
|
my $zpval = shift; # ZSK phase value.
|
|
my $phasestr; # Translated argument list.
|
|
|
|
#
|
|
# Set up the appropriate arguments to sign with.
|
|
#
|
|
if($kpval)
|
|
{
|
|
$phasestr = "KSK phase $kpval -signonly";
|
|
}
|
|
elsif($zpval)
|
|
{
|
|
$phasestr = "ZSK phase $zpval -signonly";
|
|
}
|
|
else
|
|
{
|
|
$phasestr = ' -signonly';
|
|
}
|
|
|
|
return($phasestr);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_int_handler()
|
|
#
|
|
# Purpose: Remember that a sig INT was received for later processing.
|
|
#
|
|
sub queue_int_handler
|
|
{
|
|
$queued_int = 1;
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: queue_hup_handler()
|
|
#
|
|
# Purpose: Remember that a sig HUP was received for later processing.
|
|
#
|
|
sub queue_hup_handler
|
|
{
|
|
$queued_hup = 1;
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: intcmd_handler()
|
|
#
|
|
# Purpose: Handle an interrupt and get a command.
|
|
#
|
|
sub intcmd_handler
|
|
{
|
|
rolllog_log(LOG_TMI,'<command>',"rollover manager: got a command interrupt\n");
|
|
|
|
controllers(0);
|
|
commander();
|
|
controllers(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: halt_handler()
|
|
#
|
|
# Purpose: Handle the "halt" command.
|
|
#
|
|
sub halt_handler
|
|
{
|
|
display("halt \"rollerd\" 0");
|
|
|
|
rolllog_log(LOG_ALWAYS,"","rollover manager shutting down...\n");
|
|
rollrec_write(1); # dump the current file with commands
|
|
rollmgr_rmid();
|
|
exit(0);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: chrrdir()
|
|
#
|
|
# Purpose: Move into a rollrec's directory, if it has one defined.
|
|
#
|
|
sub chrrdir
|
|
{
|
|
my $rname = shift; # Rollrec's name.
|
|
my $rrdir = shift; # Rollrec's directory.
|
|
my $id = shift; # Call identity. (optional)
|
|
|
|
#
|
|
# Don't do anything if the rollrec doesn't have a directory defined.
|
|
#
|
|
return(1) if(!defined($rrdir));
|
|
|
|
#
|
|
# Ensure we've got some trailing space at the end of the id string.
|
|
#
|
|
$id .= ' ' if($id !~ /\s$/);
|
|
|
|
#
|
|
# First we'll move into the execution directory, in case the rollrec
|
|
# directory isn't absolute.
|
|
#
|
|
chdir($xqtdir);
|
|
|
|
#
|
|
# Try to move into the rollrec's directory. If we can't, we'll
|
|
# return a failure.
|
|
#
|
|
if(chdir($rrdir) == 0)
|
|
{
|
|
|
|
rolllog_log(LOG_ERR,$rname,$id . "invalid rollrec directory \"$rrdir\"; skipping...");
|
|
|
|
return(0);
|
|
}
|
|
|
|
rolllog_log(LOG_TMI,$rname,$id . "using rollrec directory \"$rrdir\"");
|
|
return(1);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: cleanup()
|
|
#
|
|
# Purpose: Perform whatever clean-up is required.
|
|
#
|
|
sub cleanup
|
|
{
|
|
rolllog_log(LOG_ALWAYS,"cleaning up...") if($loglevel == LOG_TMI);
|
|
rollmgr_rmid();
|
|
exit(0);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: displayer()
|
|
#
|
|
# Purpose: Turn on/off the graphical display, depending on the display
|
|
# flag.
|
|
#
|
|
sub displayer
|
|
{
|
|
#
|
|
# Set the global display flag to an argument -- iff we got one.
|
|
#
|
|
if(@_ == 1)
|
|
{
|
|
$display = shift;
|
|
}
|
|
|
|
#
|
|
# Turn on or off the graphical display, depending on the display flag.
|
|
#
|
|
if($display)
|
|
{
|
|
open(DISPLAY, "|blinkenlights $rollrecfile");
|
|
}
|
|
else
|
|
{
|
|
display("halt \"rollerd\" 1");
|
|
close(DISPLAY);
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: display()
|
|
#
|
|
# Purpose: Send a message to the display program iff the display flag
|
|
# is on. If we couldn't send the message, then we'll turn
|
|
# the flag off.
|
|
#
|
|
sub display
|
|
{
|
|
my $dmsg = shift; # Message to send.
|
|
my $savedsel; # Saved descriptor.
|
|
my $ret; # Retcode from print.
|
|
|
|
#
|
|
# Don't try anything if we aren't connected to a display program.
|
|
#
|
|
# return if(!$display);
|
|
return if(fileno(DISPLAY) == -1);
|
|
|
|
#
|
|
# Force this message to be unbuffered.
|
|
#
|
|
$savedsel = select(DISPLAY);
|
|
$| = 1;
|
|
$ret = print DISPLAY "$dmsg\n";
|
|
select($savedsel);
|
|
|
|
if(!$ret)
|
|
{
|
|
rolllog_log(LOG_INFO,'<command>',"unable to send message to blinkenlights; turning off display");
|
|
$display = 0;
|
|
}
|
|
}
|
|
|
|
#----------------------------------------------------------------------
|
|
# Routine: version()
|
|
#
|
|
# Purpose: Print the version number(s) and exit.
|
|
#
|
|
sub version
|
|
{
|
|
print STDERR "$VERS\n";
|
|
print STDERR "$DTVERS\n";
|
|
|
|
exit(0);
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Routine: usage()
|
|
#
|
|
# Purpose: Print a usage message and exit.
|
|
#
|
|
sub usage
|
|
{
|
|
print STDERR "usage: rollerd [options]\n";
|
|
print STDERR "\toptions:\n";
|
|
print STDERR "\t\t-rrfile <rollrec-file>\n";
|
|
print STDERR "\t\t-directory <dir>\n";
|
|
print STDERR "\t\t-logfile <logfile>\n";
|
|
print STDERR "\t\t-loglevel <level>\n";
|
|
print STDERR "\t\t-noreload\n";
|
|
print STDERR "\t\t-pidfile <pidfile>\n";
|
|
print STDERR "\t\t-sleep <sleeptime>\n";
|
|
print STDERR "\t\t-dtconfig <dnssec-tools-config-file>\n";
|
|
print STDERR "\t\t-zonesigner <full-path-to-zonesigner>\n";
|
|
print STDERR "\t\t-display\n";
|
|
print STDERR "\t\t-parameters\n";
|
|
print STDERR "\t\t-autosign | -noautosign\n";
|
|
print STDERR "\t\t-singlerun\n";
|
|
print STDERR "\t\t-foreground\n";
|
|
print STDERR "\t\t-alwayssign\n";
|
|
print STDERR "\t\t-username <username>\n";
|
|
print STDERR "\t\t-zsargs <argument-list>\n";
|
|
print STDERR "\t\t-verbose\n";
|
|
print STDERR "\t\t-Version\n";
|
|
print STDERR "\t\t-help\n";
|
|
exit(0);
|
|
}
|
|
|
|
1;
|
|
|
|
##############################################################################
|
|
#
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
rollerd - DNSSEC-Tools daemon to manage DNSSEC key rollover
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
rollerd [-options] -rrfile <rollrec_file>
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The B<rollerd> daemon manages key rollover for zones. B<rollerd> is just a
|
|
scheduler for zone rollover; it uses B<zonesigner> to perform the actual key
|
|
generation, zone signing, and key manipulation.
|
|
|
|
B<rollerd> manages both KSK and ZSK rollover, though only one rollover type
|
|
per zone may take place at a time. Initiation of KSK rollovers takes
|
|
precedence over the initiation of ZSK rollovers.
|
|
|
|
B<rollerd> uses two methods of key rollover. The Pre-Publish Method of key
|
|
rollover is used for ZSK key rollovers. The Double Signature Method of key
|
|
rollover is used for KSK rollovers. These methods are described more fully
|
|
below.
|
|
|
|
B<rollerd> maintains zone rollover state in files called I<rollrec> files;
|
|
zone/key mappings are stored in I<keyrec> files. B<rollerd> only modifies
|
|
I<rollrec> files. For the most part, B<rollerd> does not modify I<keyrec>
|
|
directly, but relies on B<zonesigner> to update those files as needed.
|
|
(The exceptions where B<rollerd> modifies I<keyrec> files. At start-up,
|
|
it will mark each managed zone's I<keyrec> file to indicate the zone is under
|
|
B<rollerd>'s control. During the course of rollover, B<rollerd> will also
|
|
update a zone's rollover times in its I<keyrec> file.)
|
|
|
|
The administrator may control B<rollerd> with the B<rollctl> command. A large
|
|
number of commands are available to control and modify B<rollerd>'s operation,
|
|
as well as to retrieve information about rollover and daemon status.
|
|
|
|
The zone administrator will need to update their zone files periodically. If
|
|
B<rollerd> is managing zones, then problems could arise if modified zones were
|
|
signed without B<rollerd>'s knowledge. To prevent such problems, B<rollerd>
|
|
can be configured to automatically re-sign a zone when its zonefile is found
|
|
to be newer than its corresponding signed zonefile. (The files' "last
|
|
modification" timestamps are compared.) The zone will be re-signed without
|
|
any other rollover actions taking place, so no new keys will be generated, no
|
|
key rollovers will occur, and the various rollover timers will be unaffected.
|
|
|
|
B<rollerd> will perform these re-signs by default, but this can be further
|
|
controlled by the I<autosign> configuration parameter and B<rollerd>'s
|
|
B<-autosign>/B<-noautosign> command line options. If B<rollerd> is configured
|
|
to not perform automatic re-signing, the administrator can still get this
|
|
controlled behavior by use of the B<zonesigner> or B<rollctl> commands.
|
|
|
|
If the B<rollrec> file does not exist or is of zero length, B<rollerd> will
|
|
give an error message and continue running. It will periodically wake up and
|
|
check for a usable B<rollrec> file. Once it finds the specified B<rollrec>
|
|
file exists and isn't empty, then it will proceed with normal rollover
|
|
management.
|
|
|
|
=head2 ZSK Rollover Using the Pre-Publish Method
|
|
|
|
The Pre-Publish Method has four phases that are entered when it is time to
|
|
perform ZSK rollover:
|
|
|
|
1. wait for old zone data to expire from caches
|
|
2. sign the zone with the KSK and Published ZSK
|
|
3. wait for old zone data to expire from caches
|
|
4. adjust keys in keyrec and sign the zone with new Current ZSK
|
|
|
|
B<rollerd> uses the B<zonesigner> command during ZSK rollover phases 2 and 4.
|
|
B<zonesigner> will generate keys as required and sign the zone during these
|
|
two phases.
|
|
|
|
The Pre-Publish Method of key rollover is defined in the Step-by-Step DNS
|
|
Security Operator Guidance Document. See that document for more detailed
|
|
information.
|
|
|
|
=head2 KSK Rollover Using the Double Signature Method
|
|
|
|
The Double Signature Method has seven phases that are entered when it is time
|
|
to perform KSK rollover:
|
|
|
|
1 - wait for cache data to expire
|
|
2 - generate a new (published) KSK and load zone
|
|
3 - wait for the old DNSKEY RRset to expire from caches
|
|
4 - transfer new keyset to the parent
|
|
5 - wait for parent to publish DS record
|
|
6 - wait for cache data to expire
|
|
7 - roll the KSKs and load the zone
|
|
|
|
This is a modification of the original Double Signature Method. In the
|
|
original method, the KSK keys were rolled before the new keyset was
|
|
transferred to the parent. The new method rolls the KSK keys after the
|
|
parent has published the new DS records and old data has expired for caches.
|
|
This addresses a potential problem with signing zones during key rollovers.
|
|
|
|
B<rollerd> uses the B<zonesigner> command during KSK rollover phases 2 and 7.
|
|
B<zonesigner> will generate keys as required and sign the zone during these
|
|
two phases.
|
|
|
|
Currently, leaving the waiting period in step 5 is handled manually. In step
|
|
4, B<rollerd> informs the administrator via email that the zone's keyset must
|
|
be transferred to its parent in order for rollover to continue. In step 5,
|
|
after the keyset has been transferred to the parent and the parent has
|
|
published a new DS record, the administrator uses B<rollctl> to inform
|
|
B<rollerd> that the DS record has been published and rollover may continue.
|
|
|
|
The Double Signature Method of key rollover is defined in the Step-by-Step
|
|
DNS Security Operator Guidance Document. See that document for more detailed
|
|
information.
|
|
|
|
=head2 KSK Rollover Using the Double Signature Method and RFC5011
|
|
|
|
RFC5011 describes how remote-validating resolvers must track KSK changes
|
|
within a zone. If configured for RFC5011 behavior, B<rollerd> and
|
|
B<zonesigner> add an extra-long period of time between the point a new KSK is
|
|
created and published and the point where the actual switch to using it takes
|
|
place. RFC5011 specifies that remote validators should add a "hold-down
|
|
timer" to the rollover process, such that the new key is not added as a
|
|
trust-anchor until 30 days have past. Thus, B<rollerd> will wait for 60 days
|
|
(by default) during phase 3 of the KSK rollover process if the "istrustanchor"
|
|
field of the I<rollrec> definition has been set to either 1 or "yes". To wait
|
|
for a different length of time other than 60 days, use the B<holddowntime>
|
|
field.
|
|
|
|
At this time, the other conventions of RFC5011 are not being followed.
|
|
Specifically, it's not waiting for a while before removing the old key
|
|
and it's not adding the revoke bit to the old key after switching.
|
|
|
|
=head2 Site-Specific Rollover Actions
|
|
|
|
An administrator can specify site-specific commands to be run during the
|
|
various rollover phases. The commands can be run in place of the default
|
|
B<rollerd> rollover actions, or in addition to them. This subsection
|
|
describes how to make use of site-specific rollover actions.
|
|
|
|
This capability is provided to allow different installations to handle
|
|
rollover according to their specific needs. For example, it is anticipated
|
|
that this might be helpful to sites using HSM hardware, or to allow for
|
|
enhanced reporting to administrators. This has been used with simple test
|
|
programs to ensure that it actually works. However, it has not yet been used
|
|
in actual HSM environment or with other production-level software
|
|
replacements.
|
|
|
|
See the I<ZSK Rollover Using the Pre-Publish Method> and I<KSK Rollover
|
|
Using the Double Signature Method> sections for descriptions of the default
|
|
rollover actions.
|
|
|
|
B<WARNING:> This has the potential of being a dangerous capability. Be
|
|
I<very> careful when setting up and using it. Take care with the site-specific
|
|
commands to be executed and the permissions and ownership of B<rollerd> and
|
|
its data files.
|
|
|
|
=head3 DNSSEC-Tools Configuration File Changes
|
|
|
|
The DNSSEC-Tools configuration file must be modified to tell B<rollerd> what
|
|
must be run for the non-default rollover phase actions. Key/value pairs may
|
|
be set for each rollover phase to control how that phase differs from the
|
|
default.
|
|
|
|
The value portion of the configuration entry contains the path to the
|
|
site-specific phase command, along with any arguments it might need.
|
|
Multiple commands are separated by bangs.
|
|
|
|
The reserved I<default> command tells B<rollerd> to use its normal rollover
|
|
action for a particular phase. This may be combined with other commands to
|
|
provide things such as specialized logging or notifications.
|
|
|
|
B<rollerd> will only alter the behavior of a rollover phase if the
|
|
configuration file contains an entry for that phase. If not, the default
|
|
action will be taken.
|
|
|
|
For example, this configuration line tells B<rollerd> that for ZSK rollover
|
|
phase 2, instead of using its normal B<zonesigner> executions it should run
|
|
the B<hsm-signer> command.
|
|
|
|
prog-zsk2 hsm-signer
|
|
|
|
In this example, this configuration line informs B<rollerd> that when entering
|
|
KSK rollover phase 1 and ZSK rollover phase 1, it should execute the
|
|
B<log-and-mail> command, then use the normal rollover action for those phases.
|
|
|
|
prog-ksk1 /usr/local/sbin/log-and-mail mary ! default
|
|
prog-zsk1 /usr/local/sbin/log-and-mail bob!default
|
|
|
|
The following configuration keys are used for controlling KSK rollover
|
|
phases: I<prog-ksk1>, I<prog-ksk2>, I<prog-ksk3>, I<prog-ksk4>, I<prog-ksk5>,
|
|
I<prog-ksk6>, and I<prog-ksk7>,
|
|
|
|
The following configuration keys are used for controlling ZSK rollover phases:
|
|
I<prog-zsk1>, I<prog-zsk2>, I<prog-zsk3>, and I<prog-zsk4>.
|
|
|
|
The I<prog-normal> configuration key controls the normal, non-rollover state.
|
|
|
|
=head3 Site-Specific Commands
|
|
|
|
To be generally useful, the site-specific commands executed by B<rollerd>
|
|
will be given a standard set of arguments, and a standard set of exit
|
|
values will be recognized.
|
|
|
|
The standard arguments from B<rollerd> are:
|
|
1. zonename - Zone to be handled.
|
|
2. phase - Zone's current rollover phase (e.g., zsk1, ksk6, normal.)
|
|
3. rollrec name - Zone's entry key in the rollrec file.
|
|
4. rollrec file - The path to the rollrec file.
|
|
5. keyrec file - The path to the zone's keyrec file.
|
|
|
|
The I<prog-phase> entry in the configuration file may specify additional
|
|
options and arguments for a command. These will be included on the execution
|
|
command line I<prior> to the standard arguments.
|
|
|
|
The standard exit values expected by B<rollerd> are:
|
|
0. The zone can move to the next rollover phase.
|
|
This is only applicable to the current command; other
|
|
commands in this phase's command list must still be run.
|
|
1. The zone should stay in the current rollover phase.
|
|
This is not necessarily the result of an error.
|
|
2. An error was found in the arguments given to the command.
|
|
3. An error was encountered during execution.
|
|
|
|
If a rollover phase's configuration entry lists multiple commands, they will
|
|
be executed in the order listed. If any command in that command list fails,
|
|
processing stops there.
|
|
|
|
The B<rp-wrapper> command shows how a site-specific command may be written.
|
|
B<rp-wrapper> may be used as a skeleton on which to build a more useful
|
|
rollover-phase command.
|
|
|
|
=head3 Considerations for Site-Specific Commands
|
|
|
|
The following should be taken into consideration when writing a site-specific
|
|
command for a rollover phase.
|
|
|
|
=over 4
|
|
|
|
=item execution length
|
|
|
|
A phase command should not execute very long. As currently written, B<rollerd>
|
|
serializes zone rollover. So the longer a phase command takes to execute, the
|
|
longer it will take to get to the next zone. If a phase command sleeps or
|
|
actively waits, so to speak, for the next phase timeout, then every zone
|
|
B<rollerd> manages will be left waiting.
|
|
|
|
=item follow interface guidelines
|
|
|
|
Follow the standards for arguments and exit values. Not following the
|
|
standards is likely to negatively affect zone rollover.
|
|
|
|
=item frequency of command execution
|
|
|
|
If B<rollerd> is operating in its traditional "full list" processing mode, a
|
|
phase command list will be executed every time B<rollerd> cycles through its
|
|
zone list and a zone is in that particular command's phase. For example, if
|
|
<i>prog_zsk1</i> is defined for example.com, that command list will be
|
|
executed for example.com every time B<rollerd> runs its zone list and finds
|
|
example.com is in the ZSK phase 1 rollover state. A phase command B<must> take
|
|
this into account so it doesn't perform its actions more frequently than
|
|
necessary. This is most likely an issue for the various rollover wait states,
|
|
and possibly the normal state.
|
|
|
|
If B<rollerd> is operating in the experimental "soon queue" processing mode, a
|
|
phase command list will be executed for a zone only when a phase change
|
|
occurs. Since phase changes are time queued, this should not happen more than
|
|
once per phase. A phase command B<should> take this into account, in case the
|
|
soon queue is reordered before the zone leaves the queue, or queue timing is
|
|
relatively swift. This is most likely an issue for the various rollover wait
|
|
states.
|
|
|
|
B<WARNING:> "soon queue" processing is I<experimental>. Care should be taken
|
|
when using this processing method, as it may still have some lingering bugs.
|
|
|
|
=back
|
|
|
|
=head2 Zone Reloading
|
|
|
|
B<rollerd> has the opportunity to inform the DNS daemon to reload a zone in
|
|
KSK phase 2, KSK phase 7, ZSK phase 2, and ZSK phase 4. This is the
|
|
B<rollerd>'s default behavior. However, there are situations where this
|
|
shouldn't be done, such as for off-line signing.
|
|
|
|
The B<roll_loadzone> field of the DNSSEC-Tools configuration file is a boolean
|
|
field that overrides the default to force the zone-reload behavior either on
|
|
or off. This field takes precedence over the default.
|
|
|
|
Similarly, the B<-noreload> option prevents B<rollerd> from requesting a zone
|
|
reload, and it takes precedence over the B<roll_loadzone> configuration field
|
|
and the default.
|
|
|
|
=head2 I<rollrec> Files
|
|
|
|
The zones to be managed by B<rollerd> are described in a I<rollrec>
|
|
file. Generally speaking most people will want to use the B<rollinit>
|
|
command to create an initial I<rollrec> file instead of typing their
|
|
own from scratch. See the INITIALIZATION AND USAGE section below and
|
|
the B<rollinit> manual page for details. Each zone's entry contains
|
|
data needed by B<rollerd> and some data useful to a user. Below is a
|
|
sample I<rollrec> entry:
|
|
|
|
roll "example.com"
|
|
zonename "example.com"
|
|
zonefile "example.com.signed"
|
|
keyrec "example.com.krf"
|
|
zonegroup "demo-zones"
|
|
directory "dir-example.com"
|
|
kskphase "0"
|
|
zskphase "3"
|
|
ksk_rollsecs "1172614842"
|
|
ksk_rolldate "Tue Feb 27 22:20:42 2007"
|
|
zsk_rollsecs "1172615087"
|
|
zsk_rolldate "Tue Feb 27 22:24:47 2007"
|
|
maxttl "60"
|
|
display "1"
|
|
phasestart "Tue Feb 27 22:25:07 2007"
|
|
# optional records for RFC5011 rolling:
|
|
istrustanchor "no"
|
|
holddowntime "60D"
|
|
|
|
The first line gives the I<rollrec> entry's name. The name distinguishes it
|
|
from other I<rollrec> entries and must be unique. This may be the zone's
|
|
name, but this is not a requirement. The following lines give the zone's
|
|
name, the zone's signed zone file, I<keyrec> file, the current rollover
|
|
phases, the rollover timestamps, and other information. The zone group is
|
|
optional and allows a set of related zones to be controlled with a single
|
|
B<rollctl> execution, rather than one execution per zone.
|
|
|
|
If either of the I<zonefile> or I<keyrec> files do not exist, then a "roll"
|
|
I<rollrec> will be changed into a "skip" I<rollrec>. That record will not be
|
|
processed.
|
|
|
|
A more detailed explanation may be found in I<rollrec(5)>.
|
|
|
|
=head2 Directories
|
|
|
|
B<rollerd>'s execution directory is either the directory in which it is
|
|
executed or the directory passed in the B<-directory> command-line option.
|
|
Any files used by B<rollerd> that were not specified with absolute paths use
|
|
this directory as their base.
|
|
|
|
A I<rollrec> file's I<directory> field informs B<rollerd> where the zone's
|
|
files may be found. For that zone, B<rollerd> will move into that directory,
|
|
then return to its execution directory when it finishes rollover operations
|
|
for that zone. If the I<directory> value is a relative path, it will be
|
|
appended to B<rollerd>'s execution directory. If the I<directory> value is an
|
|
absolute path, it will be used as is.
|
|
|
|
=head2 Controlling B<rollerd> with B<rollctl>
|
|
|
|
The B<rollctl> command is used to control the behavior of B<rollerd>. A
|
|
number of commands are available, such as starting or stopping rollover for a
|
|
selected zone or all zones, turning on or off a GUI rollover display, and
|
|
halting B<rollerd> execution. The communications path between B<rollerd> and
|
|
B<rollctl> is operating system-dependent. On Unix-like systems, it is a Unix
|
|
pipe that should B<only> be writable by the user which runs I<rollerd>. A
|
|
more detailed explanation of B<rollctl> may be found in I<rollctl(8)>.
|
|
|
|
=head2 A Note About Files and Filenames
|
|
|
|
There are a number of files and filenames used by B<rollerd> and
|
|
B<zonesigner>. The user must be aware of the files used by these programs,
|
|
where the files are located, and where the programs are executed.
|
|
|
|
By default, B<rollerd> will change directory to the DNSSEC-Tools directory,
|
|
though this may be changed by the B<-directory> option. Any programs started
|
|
by B<rollerd>, most importantly B<zonesigner>, will run in this same
|
|
directory. If files and directories referenced by these programs are named
|
|
with relative paths, those paths must be relative to this directory.
|
|
|
|
The I<rollrec> entry name is used as a key to the I<rollrec> file and to the
|
|
zone's I<keyrec> file. This entry does not have to be the name of the
|
|
entry's domain, but it is a very good idea to make it so. Whatever is used
|
|
for this entry name, the same name B<must> be used for the zone I<keyrec> in
|
|
that zone's I<keyrec> file.
|
|
|
|
It is probably easiest to store I<rollrec> files, I<keyrec> files, zone
|
|
files, and key files in a single directory.
|
|
|
|
=head1 INITIALIZATION AND USAGE
|
|
|
|
The following steps must be taken to initialize and use B<rollerd>. This
|
|
assumes that zone files have been created, and that BIND and DNSSEC-Tools
|
|
have been installed.
|
|
|
|
=over 4
|
|
|
|
=item 0. sign zones
|
|
|
|
The zones to be managed by B<rollerd> must be signed. Use B<zonesigner> to
|
|
create the signed zone files and the I<keyrec> files needed by B<rollerd>.
|
|
The I<rollrec> file created in the next step B<must> use the I<keyrec> file
|
|
names and the signed zone file names created here.
|
|
|
|
This step is optional. If it is bypassed, then (in step 4 and later)
|
|
B<rollerd> will perform the initial key creation and zone signing of your
|
|
zones using the defaults found in the DNSSEC-Tools configuration file.
|
|
B<rollerd> determines if it must perform these initial operations by whether
|
|
it can find the I<keyrec> file for a zone (as specified in the I<rollrec>
|
|
file. If it can't, it performs the initial operations; if it can, it assumes
|
|
the zone's initial operations have been performed.
|
|
|
|
=item 1. create I<rollrec> file
|
|
|
|
Before B<rollerd> may be used, a I<rollrec> file must first be created.
|
|
While this file may be built by hand, the B<rollinit> command was
|
|
written specifically to build the file.
|
|
|
|
=item 2. select operational parameters
|
|
|
|
A number of B<rollerd>'s operational parameters are taken from the
|
|
DNSSEC-Tools configuration file. However, these may be overridden
|
|
by command-line options. See the OPTIONS section below for more details.
|
|
If non-standard parameters are desired to always be used, the appropriate
|
|
fields in the DNSSEC-Tools configuration file may be modified to use these
|
|
values.
|
|
|
|
=item 3. install the rollover configuration
|
|
|
|
The complete rollover configuration -- B<rollerd>, I<rollrec> file,
|
|
DNSSEC-Tools configuration file values, zone files -- should be installed.
|
|
The appropriate places for these locations are both installation-dependent
|
|
and operating system-dependent.
|
|
|
|
=item 4. test the rollover configuration
|
|
|
|
The complete rollover configuration should be tested.
|
|
|
|
Edit the zone files so that their zones have short TTL values. A minute TTL
|
|
should be sufficient. Test rollovers of this speed should B<only> be done
|
|
in a test environment without the real signed zone.
|
|
|
|
Run the following command:
|
|
|
|
rollerd -rrfile test.rollrec -logfile - -loglevel info -sleep 60
|
|
|
|
This command assumes the test I<rollrec> file is B<test.rollrec>. It writes
|
|
a fair amount of log messages to the terminal, and checks its queue every 60
|
|
seconds. Follow the messages to ensure that the appropriate actions, as
|
|
required by the Pre-Publish Method, are taking place.
|
|
|
|
=item 5. set B<rollerd> to start at boot
|
|
|
|
Once the configuration is found to work, B<rollerd> should be set to start
|
|
at system boot. The actual operations required for this step are operating
|
|
system-dependent.
|
|
|
|
=item 6. reboot and verify
|
|
|
|
The system should be rebooted and the B<rollerd> logfile checked to ensure
|
|
that B<rollerd> is operating properly.
|
|
|
|
=back
|
|
|
|
=head1 OPTIONS
|
|
|
|
There are a number of operational parameters that define how B<rollerd> works.
|
|
These parameters define things such as the I<rollrec> file, the logging level,
|
|
and the log file. These parameters can be set in the DNSSEC-Tools
|
|
configuration file or given as options on the B<rollerd> command line.
|
|
The command line options override values in the configuration file.
|
|
|
|
The following options are recognized:
|
|
|
|
=over 4
|
|
|
|
=item B<-alwayssign>
|
|
|
|
Tells B<rollerd> to sign the zones that aren't in the middle of being
|
|
rolled. This allows B<rollerd> to refresh signed zone signatures and
|
|
allows complete management of zone signing to be taken over by B<rollerd>.
|
|
|
|
The downside to using this option is that all the non-rolling zones will be
|
|
signed after every sleep, which may be expensive computationally.
|
|
|
|
Note: The zone files are not updated or installed at this time.
|
|
Manual copying and installation are still needed.
|
|
|
|
=item B<-autosign> | B<-noautosign>
|
|
|
|
Automatic zone-signing flag. If this is set, then a zone's zonefile will
|
|
be re-signed (and only re-signed) if it is found to be newer than the
|
|
corresponding signed zonefile.
|
|
|
|
=item B<-directory dir>
|
|
|
|
Sets the B<rollerd> execution directory. This must be a valid directory.
|
|
|
|
=item B<-display>
|
|
|
|
Starts the B<blinkenlights> graphical display program to show the status of
|
|
zones managed by B<rollerd>.
|
|
|
|
=item B<-dtconfig config_file>
|
|
|
|
Name of an alternate DNSSEC-Tools configuration file to be processed.
|
|
If specified, this configuration file is used I<in place> of the normal
|
|
DNSSEC-Tools configuration file B<not> in addition to it. Also, it will be
|
|
handled prior to I<keyrec> files, I<rollrec> files, and command-line options.
|
|
|
|
=item B<-foreground>
|
|
|
|
Run in the foreground and do not fork into a daemon.
|
|
|
|
=item B<-logfile log_file>
|
|
|
|
Sets the B<rollerd> log file to I<log_file>.
|
|
This must be a valid logging file, meaning that if I<log_file> already
|
|
exists, it must be a regular file. The only exceptions to this are if
|
|
I<logfile> is B</dev/stdout>, B</dev/tty>, B<->. Of these three, using a
|
|
I<log_file> of B<-> is preferable since Perl will properly convert the B<->
|
|
to the process' standard output.
|
|
|
|
=item B<-loglevel level>
|
|
|
|
Sets B<rollerd>'s logging level to I<level>. B<rollmgr.pm(3)> contains a list
|
|
of the valid logging levels.
|
|
|
|
=item B<-noreload>
|
|
|
|
Prevents B<rollerd> from telling the DNS daemon to reload zones.
|
|
|
|
=item B<-parameters>
|
|
|
|
Prints a set of B<rollerd> parameters and then exits. This shows the
|
|
parameters with which B<rollerd> will execute, but very little parameter
|
|
validation is performed.
|
|
|
|
=item B<-pidfile pid_file>
|
|
|
|
Stores the running process PID into I<pid_file>. This defaults to
|
|
B</var/run/rollerd.pid> on most systems.
|
|
|
|
=item B<-rrfile rollrec_file>
|
|
|
|
Name of the I<rollrec> file to be processed. This is the only required
|
|
"option".
|
|
|
|
=item B<-realm realm_name>
|
|
|
|
Name of the realm in which B<rollerd> is running. This is for use with the
|
|
DNSSEC-Tools realms facility as a means of easily identifying different
|
|
instantiations of B<rollerd>. It is informational only (e.g., B<ps> output
|
|
and log files) and is not used for anything else.
|
|
|
|
=item B<-singlerun>
|
|
|
|
Processes all needed steps once and exits. This is not the ideal way to run
|
|
B<rollerd>, but it is potentially useful for environments where keying
|
|
material is only available when specific hardware tokens have been made
|
|
available.
|
|
|
|
The timing between the steps will be potentially longer since the
|
|
time between B<rollerd> runs is dependent on when B<rollerd> is executed.
|
|
"cmd" lines must be added to the I<rollrec> file to do particular actions.
|
|
|
|
The following lines should serve as examples:
|
|
|
|
cmd "rollzsk example.com"
|
|
cmd "rollksk example.com"
|
|
cmd "dspub example.com" # (for when the parent publishes
|
|
# the new ksk)
|
|
|
|
The I<-singlerun> option implicitly implies I<-foreground> as well.
|
|
|
|
=item B<-sleep sleeptime>
|
|
|
|
Sets B<rollerd>'s sleep time to I<sleeptime>. The sleep time is the amount
|
|
of time (in seconds) B<rollerd> waits between processing its I<rollrec>-based
|
|
queue.
|
|
|
|
=item B<-username username>
|
|
|
|
B<username> is the user for which the B<rollerd> daemon will be executed.
|
|
The B<rollerd> process' effective uid will be set to the uid corresponding
|
|
to B<username>.
|
|
|
|
If B<username> is a username, it must correspond to a valid uid; if it is
|
|
a uid, it must correspond to a valid username.
|
|
|
|
If B<rollerd> does not have the appropriate O/S magic (e.g., for Unix,
|
|
installed as I<setuid> program and owned by root) then it will only be able
|
|
to switch to those users to which the executing user has privilege to switch.
|
|
This restriction is dependent on the operating system and the manner by which
|
|
B<rollerd> is installed.
|
|
|
|
When using this option, the target user must have access to the various
|
|
directories, logs, and data files that B<rollerd> requires to execute.
|
|
Without this access, proper execution cannot occur.
|
|
|
|
=item B<-zsargs arglist>
|
|
|
|
Additional B<zonesigner> arguments that will be passed to all B<zonesigner>
|
|
executions. These arguments will override the corresponding arguments in the
|
|
DNSSEC-Tools configuration file, and the zones' I<keyrec> files. If a zone's
|
|
B<rollrec> entry contains a I<zsargs> field, then it will be used instead of
|
|
those specified by this argument.
|
|
|
|
Given the B<rollerd> argument processing, the new arguments for B<zonesigner>
|
|
cannot be specified as expected. Instead, the arguments should be given in
|
|
the following manner. The leading dash should be replaced with an equals sign.
|
|
If the option takes an argument, the space that would separate the option from
|
|
the option's argument should also be replaced by an equals sign. If multiple
|
|
arguments will be passed via B<-zsargs>, quotes must be used to group them
|
|
into a single argument.
|
|
|
|
B<rollerd> translates these arguments to the appropriate format for
|
|
B<zonesigner>. These examples should clarify the modifications:
|
|
|
|
normal zonesigner option rollerd -zsargs option
|
|
------------------------ ----------------------
|
|
-nokrfile -zsargs =nokrfile
|
|
-zskcount 5 -kskcount 3 -zsargs "=zskcount=5 =kskcount=3"
|
|
|
|
=item B<-Version>
|
|
|
|
Displays the version information for B<rollerd> and the DNSSEC-Tools package.
|
|
|
|
=item B<-help>
|
|
|
|
Display a usage message.
|
|
|
|
=item B<-verbose>
|
|
|
|
Verbose output will be given.
|
|
|
|
=back
|
|
|
|
=head1 ASSUMPTIONS
|
|
|
|
B<rollerd> uses the B<rndc> command to communicate with the BIND
|
|
B<named> daemon. Therefore, it assumes that appropriate measures have been
|
|
taken so that this communication is possible.
|
|
|
|
=head1 KNOWN PROBLEMS
|
|
|
|
The following problems (or potential problems) are known:
|
|
|
|
=over 4
|
|
|
|
=item -
|
|
|
|
Any process that can write to the rollover socket can send commands to
|
|
B<rollerd>. This is probably not a Good Thing.
|
|
|
|
=back
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright 2005-2014 SPARTA, Inc. All rights reserved.
|
|
See the COPYING file included with the DNSSEC-Tools package for details.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Wayne Morrison, tewok@tislabs.com
|
|
|
|
=head1 SEE ALSO
|
|
|
|
B<blinkenlights(8)>,
|
|
B<dtrealms(8)>,
|
|
B<named(8)>,
|
|
B<rndc(8)>,
|
|
B<rp-wrapper(8)>,
|
|
B<rollchk(8)>,
|
|
B<rollctl(8)>,
|
|
B<rollinit(8)>,
|
|
B<zonesigner(8)>
|
|
|
|
B<Net::DNS::SEC::Tools::conf.pm(3)>,
|
|
B<Net::DNS::SEC::Tools::defaults.pm(3)>,
|
|
B<Net::DNS::SEC::Tools::keyrec.pm(3)>,
|
|
B<Net::DNS::SEC::Tools::rolllog.pm(3)>,
|
|
B<Net::DNS::SEC::Tools::rollmgr.pm(3)>,
|
|
B<Net::DNS::SEC::Tools::rollrec.pm(3)>
|
|
|
|
B<rollrec(5)>
|
|
|
|
=cut
|