update-leap.in revision 1.1.1.4 1 1.1 christos #! @PATH_PERL@ -w
2 1.1.1.4 christos # @configure_input
3 1.1 christos
4 1.1.1.3 christos # Copyright (C) 2015, 2017 Network Time Foundation
5 1.1 christos # Author: Harlan Stenn
6 1.1.1.3 christos #
7 1.1.1.3 christos # General cleanup and https support: Paul McMath
8 1.1.1.3 christos #
9 1.1 christos # Original shell version:
10 1.1 christos # Copyright (C) 2014 Timothe Litt litt at acm dot org
11 1.1.1.3 christos #
12 1.1 christos # This script may be freely copied, used and modified providing that
13 1.1 christos # this notice and the copyright statement are included in all copies
14 1.1 christos # and derivative works. No warranty is offered, and use is entirely at
15 1.1 christos # your own risk. Bugfixes and improvements would be appreciated by the
16 1.1 christos # author.
17 1.1 christos
18 1.1.1.3 christos ######## BEGIN #########
19 1.1 christos use strict;
20 1.1 christos
21 1.1.1.3 christos # Core modules
22 1.1 christos use Digest::SHA qw(sha1_hex);
23 1.1.1.3 christos use File::Basename;
24 1.1 christos use File::Copy qw(move);
25 1.1.1.3 christos use File::Temp qw(tempfile);
26 1.1 christos use Getopt::Long qw(:config auto_help no_ignore_case bundling);
27 1.1.1.3 christos use Sys::Syslog qw(:standard :macros);
28 1.1 christos
29 1.1.1.3 christos # External modules
30 1.1.1.3 christos use HTTP::Tiny 0.056;
31 1.1.1.3 christos use Net::SSLeay 1.49;
32 1.1.1.3 christos use IO::Socket::SSL 1.56;
33 1.1.1.3 christos
34 1.1.1.3 christos my $VERSION = '1.004';
35 1.1.1.3 christos
36 1.1.1.3 christos my $RUN_DIR = '/tmp';
37 1.1.1.3 christos my $RUN_UID = 0;
38 1.1.1.3 christos my $TMP_FILE;
39 1.1.1.3 christos my $TMP_FH;
40 1.1.1.3 christos my $FILE_MODE = 0644;
41 1.1.1.3 christos
42 1.1.1.3 christos ######## DEFAULT CONFIGURATION ##########
43 1.1.1.3 christos # LEAP FILE SRC URIS
44 1.1.1.3 christos # HTTPS - (default)
45 1.1.1.3 christos # https://www.ietf.org/timezones/data/leap-seconds
46 1.1.1.3 christos # HTTP - No TLS/SSL - (not recommended)
47 1.1.1.3 christos # http://www.ietf.org/timezones/data/leap-seconds.list
48 1.1 christos
49 1.1.1.3 christos my $LEAPSRC = 'https://www.ietf.org/timezones/data/leap-seconds.list';
50 1.1 christos my $LEAPFILE;
51 1.1 christos
52 1.1 christos # How many times to try to download new file
53 1.1.1.3 christos my $MAXTRIES = 6;
54 1.1.1.3 christos my $INTERVAL = 10;
55 1.1 christos
56 1.1.1.3 christos my $NTPCONF='/etc/ntp.conf';
57 1.1 christos
58 1.1 christos # How long (in days) before expiration to get updated file
59 1.1.1.3 christos my $PREFETCH = 60;
60 1.1.1.3 christos my $EXPIRES;
61 1.1.1.3 christos my $FORCE;
62 1.1 christos
63 1.1.1.3 christos # Output Flags
64 1.1.1.3 christos my $QUIET;
65 1.1.1.3 christos my $DEBUG;
66 1.1.1.3 christos my $SYSLOG;
67 1.1.1.3 christos my $TOTERM;
68 1.1.1.3 christos my $LOGFAC = 'LOG_USER';
69 1.1.1.3 christos
70 1.1.1.3 christos ######### PARSE/SET OPTIONS #########
71 1.1.1.3 christos my %SSL_OPTS;
72 1.1.1.3 christos my %SSL_ATTRS = (
73 1.1.1.3 christos verify_SSL => 1,
74 1.1.1.3 christos SSL_options => \%SSL_OPTS,
75 1.1.1.3 christos );
76 1.1 christos
77 1.1.1.3 christos our(%opt);
78 1.1 christos
79 1.1.1.3 christos GetOptions(\%opt,
80 1.1.1.3 christos 'C=s',
81 1.1.1.3 christos 'D=s',
82 1.1.1.3 christos 'e:60',
83 1.1.1.3 christos 'F',
84 1.1.1.3 christos 'f=s',
85 1.1.1.3 christos 'h|help',
86 1.1.1.3 christos 'i:10',
87 1.1.1.3 christos 'L=s',
88 1.1.1.3 christos 'l=s',
89 1.1.1.3 christos 'q',
90 1.1.1.3 christos 'r:6',
91 1.1.1.3 christos 's',
92 1.1.1.3 christos 't',
93 1.1.1.3 christos 'u=s',
94 1.1.1.3 christos 'v',
95 1.1.1.3 christos );
96 1.1 christos
97 1.1.1.3 christos $LOGFAC = $opt{l} if defined $opt{l};
98 1.1.1.3 christos $LEAPSRC = $opt{u} if defined $opt{u};
99 1.1.1.3 christos $LEAPFILE = $opt{L} if defined $opt{L};
100 1.1.1.3 christos $PREFETCH = $opt{e} if defined $opt{e};
101 1.1.1.3 christos $NTPCONF = $opt{f} if defined $opt{f};
102 1.1.1.3 christos $MAXTRIES = $opt{r} if defined $opt{r};
103 1.1.1.3 christos $INTERVAL = $opt{i} if defined $opt{i};
104 1.1.1.3 christos
105 1.1.1.3 christos $FORCE = 1 if defined $opt{F};
106 1.1.1.3 christos $DEBUG = 1 if defined $opt{v};
107 1.1.1.3 christos $QUIET = 1 if defined $opt{q};
108 1.1.1.3 christos $SYSLOG = 1 if defined $opt{s};
109 1.1.1.3 christos $TOTERM = 1 if defined $opt{t};
110 1.1.1.3 christos
111 1.1.1.3 christos $SSL_OPTS{SSL_ca_file} = $opt{C} if (defined($opt{C}));
112 1.1.1.3 christos $SSL_OPTS{SSL_ca_path} = $opt{D} if (defined($opt{D}));
113 1.1.1.3 christos
114 1.1.1.3 christos ###############
115 1.1.1.3 christos ## START MAIN
116 1.1.1.3 christos ###############
117 1.1.1.3 christos my $PROG = basename($0);
118 1.1.1.3 christos
119 1.1.1.3 christos # Logging - Default is to use syslog(3) if STDOUT isn't
120 1.1.1.3 christos # connected to a tty.
121 1.1.1.3 christos if ($SYSLOG || !-t STDOUT) {
122 1.1.1.3 christos $SYSLOG = 1;
123 1.1.1.3 christos openlog($PROG, 'pid', $LOGFAC);
124 1.1.1.3 christos }
125 1.1.1.3 christos else {
126 1.1.1.3 christos $TOTERM = 1;
127 1.1.1.3 christos }
128 1.1 christos
129 1.1.1.3 christos if (defined $opt{q} && defined $opt{v}) {
130 1.1.1.3 christos log_fatal(LOG_ERR, '-q and -v options mutually exclusive');
131 1.1.1.3 christos }
132 1.1 christos
133 1.1.1.3 christos if (defined $opt{L} && defined $opt{f}) {
134 1.1.1.3 christos log_fatal(LOG_ERR, '-L and -f options mutually exclusive');
135 1.1.1.3 christos }
136 1.1 christos
137 1.1.1.3 christos $SIG{INT} = \&signal_catcher;
138 1.1.1.3 christos $SIG{TERM} = \&signal_catcher;
139 1.1.1.3 christos $SIG{QUIT} = \&signal_catcher;
140 1.1.1.3 christos
141 1.1.1.3 christos # Take some security precautions
142 1.1.1.3 christos close STDIN;
143 1.1.1.3 christos
144 1.1.1.3 christos # Show help
145 1.1.1.3 christos if (defined $opt{h}) {
146 1.1.1.3 christos show_help();
147 1.1.1.3 christos exit 0;
148 1.1.1.3 christos }
149 1.1 christos
150 1.1.1.3 christos if ($< != $RUN_UID) {
151 1.1.1.3 christos log_fatal(LOG_ERR, 'User ' . getpwuid($<) . " (UID $<) tried to run $PROG");
152 1.1.1.3 christos }
153 1.1 christos
154 1.1.1.3 christos chdir $RUN_DIR || log_fatal("Failed to change dir to $RUN_DIR");
155 1.1 christos
156 1.1.1.3 christos # Parse ntp.conf for path to leapfile if not set by user
157 1.1.1.3 christos if (! $LEAPFILE) {
158 1.1 christos
159 1.1.1.3 christos open my $LF, '<', $NTPCONF || log_fatal(LOG_ERR, "Can't open <$NTPCONF>: $!");
160 1.1 christos
161 1.1.1.3 christos while (<$LF>) {
162 1.1.1.3 christos chomp;
163 1.1.1.3 christos $LEAPFILE = $1 if /^ *leapfile\s+"(\S+)"/;
164 1.1.1.3 christos }
165 1.1.1.3 christos close $LF;
166 1.1 christos
167 1.1.1.3 christos if (! $LEAPFILE) {
168 1.1.1.3 christos log_fatal(LOG_ERR, "No leapfile directive in $NTPCONF; leapfile location not known");
169 1.1.1.3 christos }
170 1.1.1.3 christos }
171 1.1 christos
172 1.1.1.3 christos -s $LEAPFILE || logger(LOG_DEBUG, "Leapfile $LEAPFILE is empty");
173 1.1 christos
174 1.1.1.3 christos # Download new file if:
175 1.1.1.3 christos # 1. file doesn't exist
176 1.1.1.3 christos # 2. invoked w/ force flag (-F)
177 1.1.1.3 christos # 3. current file isn't valid
178 1.1.1.3 christos # 4. current file expired or expires soon
179 1.1 christos
180 1.1.1.3 christos if ( !-e $LEAPFILE || $FORCE || ! verifySHA($LEAPFILE) ||
181 1.1.1.3 christos ( $EXPIRES lt ( $PREFETCH * 86400 + time() ) )) {
182 1.1 christos
183 1.1.1.3 christos for (my $try = 1; $try <= $MAXTRIES; $try++) {
184 1.1.1.3 christos logger(LOG_DEBUG, "Attempting download from $LEAPSRC, try $try..");
185 1.1 christos
186 1.1.1.3 christos ($TMP_FH, $TMP_FILE) = tempfile(UNLINK => 1, SUFFIX => '.list');
187 1.1 christos
188 1.1.1.3 christos if (retrieve_file($TMP_FH)) {
189 1.1 christos
190 1.1.1.3 christos if ( verifySHA($TMP_FILE) ) {
191 1.1.1.3 christos move_file($TMP_FILE, $LEAPFILE);
192 1.1.1.3 christos chmod $FILE_MODE, $LEAPFILE;
193 1.1.1.3 christos logger(LOG_INFO, "Installed new $LEAPFILE from $LEAPSRC");
194 1.1.1.3 christos }
195 1.1.1.3 christos else {
196 1.1.1.3 christos logger(LOG_ERR, "Downloaded file $TMP_FILE rejected -- saved for diagnosis");
197 1.1.1.3 christos move_file($TMP_FILE, 'leap-seconds.list_corrupt');
198 1.1.1.3 christos exit 1;
199 1.1.1.3 christos }
200 1.1.1.3 christos # Fall through
201 1.1.1.3 christos exit 0;
202 1.1.1.3 christos }
203 1.1.1.3 christos
204 1.1.1.3 christos # Failure
205 1.1.1.3 christos unlink $TMP_FILE;
206 1.1.1.3 christos logger(LOG_INFO, "Download failed. Waiting $INTERVAL minutes before retrying...");
207 1.1.1.3 christos sleep $INTERVAL * 60 ;
208 1.1.1.3 christos }
209 1.1.1.3 christos
210 1.1.1.3 christos # Failed and out of retries
211 1.1.1.3 christos log_fatal(LOG_ERR, "Download from $LEAPSRC failed after $MAXTRIES attempts");
212 1.1.1.3 christos }
213 1.1 christos
214 1.1.1.3 christos logger(LOG_INFO, "Not time to replace $LEAPFILE");
215 1.1 christos
216 1.1.1.3 christos exit 0;
217 1.1 christos
218 1.1.1.3 christos ######## SUB ROUTINES #########
219 1.1.1.3 christos sub move_file {
220 1.1 christos
221 1.1.1.3 christos (my $src, my $dst) = @_;
222 1.1.1.3 christos
223 1.1.1.3 christos if ( move($src, $dst) ) {
224 1.1.1.3 christos logger(LOG_DEBUG, "Moved $src to $dst");
225 1.1.1.3 christos }
226 1.1.1.3 christos else {
227 1.1.1.3 christos log_fatal(LOG_ERR, "Moving $src to $dst failed: $!");
228 1.1.1.3 christos }
229 1.1.1.3 christos }
230 1.1.1.3 christos
231 1.1.1.3 christos # Removes temp file if terminating signal recv'd
232 1.1.1.3 christos sub signal_catcher {
233 1.1.1.3 christos my $signame = shift;
234 1.1.1.3 christos
235 1.1.1.3 christos close $TMP_FH;
236 1.1.1.3 christos unlink $TMP_FILE;
237 1.1.1.3 christos log_fatal(LOG_INFO, "Recv'd SIG${signame}. Terminating.");
238 1.1.1.3 christos }
239 1.1.1.3 christos
240 1.1.1.3 christos sub log_fatal {
241 1.1.1.3 christos my ($p, $msg) = @_;
242 1.1.1.3 christos logger($p, $msg);
243 1.1.1.3 christos exit 1;
244 1.1.1.3 christos }
245 1.1 christos
246 1.1 christos sub logger {
247 1.1.1.3 christos my ($p, $msg) = @_;
248 1.1.1.3 christos
249 1.1.1.3 christos # Suppress LOG_DEBUG msgs unless $DEBUG set
250 1.1.1.3 christos return if (!$DEBUG && $p eq LOG_DEBUG);
251 1.1.1.3 christos
252 1.1.1.3 christos # Suppress all but LOG_ERR msgs if $QUIET set
253 1.1.1.3 christos return if ($QUIET && $p ne LOG_ERR);
254 1.1 christos
255 1.1.1.3 christos if ($TOTERM) {
256 1.1.1.3 christos if ($p eq LOG_ERR) { # errors should go to STDERR
257 1.1.1.3 christos print STDERR "$msg\n";
258 1.1.1.3 christos }
259 1.1.1.3 christos else {
260 1.1.1.3 christos print STDOUT "$msg\n";
261 1.1 christos }
262 1.1 christos }
263 1.1 christos
264 1.1.1.3 christos if ($SYSLOG) {
265 1.1.1.3 christos syslog($p, $msg)
266 1.1 christos }
267 1.1 christos }
268 1.1 christos
269 1.1.1.3 christos #################################
270 1.1.1.3 christos # Connect to server and retrieve file
271 1.1.1.3 christos #
272 1.1.1.3 christos # Since we make as many as $MAXTRIES attempts to connect to the remote
273 1.1.1.3 christos # server to download the file, the network socket should be closed after
274 1.1.1.3 christos # each attempt, rather than let it be reused (because it may be in some
275 1.1.1.3 christos # unknown state).
276 1.1.1.3 christos #
277 1.1.1.3 christos # HTTP::Tiny doesn't export a method to explicitly close a connected
278 1.1.1.3 christos # socket, therefore, we instantiate the lexically scoped $http object in
279 1.1.1.3 christos # a function; when the function returns, the object goes out of scope
280 1.1.1.3 christos # and is destroyed, closing the socket.
281 1.1.1.3 christos sub retrieve_file {
282 1.1.1.3 christos
283 1.1.1.3 christos my $fh = shift;
284 1.1.1.3 christos my $http;
285 1.1.1.3 christos
286 1.1.1.3 christos if ($LEAPSRC =~ /^https\S+/) {
287 1.1.1.3 christos $http = HTTP::Tiny->new(%SSL_ATTRS);
288 1.1.1.3 christos (my $ok, my $why) = $http->can_ssl;
289 1.1.1.3 christos log_fatal(LOG_ERR, "TLS/SSL config error: $why") if ! $ok;
290 1.1.1.3 christos }
291 1.1.1.3 christos else {
292 1.1.1.3 christos $http = HTTP::Tiny->new();
293 1.1.1.3 christos }
294 1.1.1.3 christos
295 1.1.1.3 christos my $reply = $http->get($LEAPSRC);
296 1.1.1.3 christos
297 1.1.1.3 christos if ($reply->{success}) {
298 1.1.1.3 christos logger(LOG_DEBUG, "Download of $LEAPSRC succeeded");
299 1.1.1.3 christos print $fh $reply->{content} ||
300 1.1.1.3 christos log_fatal(LOG_ERR, "Couldn't write new file contents to temp file: $!");
301 1.1.1.3 christos close $fh;
302 1.1.1.3 christos return 1;
303 1.1.1.3 christos }
304 1.1.1.3 christos else {
305 1.1.1.3 christos close $fh;
306 1.1.1.3 christos return 0;
307 1.1.1.3 christos }
308 1.1.1.3 christos }
309 1.1 christos
310 1.1.1.3 christos ########################
311 1.1 christos # Validate a leap-seconds file checksum
312 1.1 christos #
313 1.1.1.3 christos # File format: (full description in file)
314 1.1.1.3 christos # Pound sign (#) marks comments, EXCEPT:
315 1.1.1.3 christos # #$ number : the NTP date of the last update
316 1.1.1.3 christos # #@ number : the NTP date that the file expires
317 1.1.1.3 christos # #h hex hex hex hex hex : the SHA-1 checksum of the data & dates,
318 1.1.1.3 christos # excluding whitespace w/o leading zeroes
319 1.1.1.3 christos #
320 1.1.1.3 christos # Date (seconds since 1900) leaps : leaps is the # of seconds to add
321 1.1.1.3 christos # for times >= Date
322 1.1 christos # Date lines have comments.
323 1.1 christos #
324 1.1 christos # Returns:
325 1.1.1.3 christos # 0 Invalid Checksum/Expired
326 1.1.1.3 christos # 1 File is valid
327 1.1 christos
328 1.1 christos sub verifySHA {
329 1.1 christos
330 1.1.1.3 christos my $file = shift;
331 1.1.1.3 christos my $fh;
332 1.1.1.3 christos my $data;
333 1.1 christos my $FSHA;
334 1.1 christos
335 1.1.1.3 christos open $fh, '<', $file || log_fatal(LOG_ERR, "Can't open $file: $!");
336 1.1.1.3 christos
337 1.1 christos # Remove comments, except those that are markers for last update,
338 1.1 christos # expires and hash
339 1.1.1.3 christos while (<$fh>) {
340 1.1 christos if (/^#\$/) {
341 1.1.1.3 christos s/^..//;
342 1.1.1.3 christos $data .= $_;
343 1.1 christos }
344 1.1 christos elsif (/^#\@/) {
345 1.1.1.3 christos s/^..//;
346 1.1.1.3 christos $data .= $_;
347 1.1.1.3 christos s/\s+//g;
348 1.1.1.3 christos $EXPIRES = $_ - 2208988800;
349 1.1 christos }
350 1.1 christos elsif (/^#h\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)/) {
351 1.1.1.3 christos chomp;
352 1.1.1.3 christos $FSHA = sprintf("%08s%08s%08s%08s%08s", $1, $2, $3, $4, $5);
353 1.1 christos }
354 1.1 christos elsif (/^#/) {
355 1.1.1.3 christos # ignore it
356 1.1 christos }
357 1.1 christos elsif (/^\d/) {
358 1.1.1.3 christos s/#.*$//;
359 1.1.1.3 christos $data .= $_;
360 1.1.1.3 christos }
361 1.1.1.3 christos else {
362 1.1.1.3 christos chomp;
363 1.1.1.3 christos print "Unexpected line: <$_>\n";
364 1.1 christos }
365 1.1 christos }
366 1.1.1.3 christos close $fh;
367 1.1 christos
368 1.1 christos if ( $EXPIRES < time() ) {
369 1.1.1.3 christos logger(LOG_DEBUG, 'File expired on ' . gmtime($EXPIRES));
370 1.1.1.3 christos return 0;
371 1.1 christos }
372 1.1 christos
373 1.1.1.3 christos if (! $FSHA) {
374 1.1.1.3 christos logger(LOG_NOTICE, "no checksum record found in file");
375 1.1.1.3 christos return 0;
376 1.1 christos }
377 1.1 christos
378 1.1.1.3 christos # Remove all white space
379 1.1.1.3 christos $data =~ s/\s//g;
380 1.1 christos
381 1.1.1.3 christos # Compute the SHA hash of the data, removing the marker and filename
382 1.1.1.3 christos # Computed in binary mode, which shouldn't matter since whitespace has been removed
383 1.1.1.3 christos my $DSHA = sha1_hex($data);
384 1.1 christos
385 1.1.1.3 christos if ($FSHA eq $DSHA) {
386 1.1.1.3 christos logger(LOG_DEBUG, "Checksum of $file validated");
387 1.1.1.3 christos return 1;
388 1.1.1.3 christos }
389 1.1.1.3 christos else {
390 1.1.1.3 christos logger(LOG_NOTICE, "Checksum of $file is invalid EXPECTED: $FSHA COMPUTED: $DSHA");
391 1.1.1.3 christos return 0;
392 1.1 christos }
393 1.1 christos }
394 1.1 christos
395 1.1.1.3 christos sub show_help {
396 1.1.1.3 christos print <<EOF
397 1.1 christos
398 1.1.1.3 christos Usage: $PROG [options]
399 1.1 christos
400 1.1.1.3 christos Verifies and if necessary, updates leap-second definition file
401 1.1 christos
402 1.1.1.3 christos All arguments are optional: Default (or current value) shown:
403 1.1.1.3 christos -C Absolute path to CA Cert (see SSL/TLS Considerations)
404 1.1.1.3 christos -D Path to a CAdir (see SSL/TLS Considerations)
405 1.1.1.3 christos -e Specify how long (in days) before expiration the file is to be
406 1.1.1.3 christos refreshed. Note that larger values imply more frequent refreshes.
407 1.1.1.3 christos $PREFETCH
408 1.1.1.3 christos -F Force update even if current file is OK and not close to expiring.
409 1.1.1.3 christos -f Absolute path ntp.conf file (default /etc/ntp.conf)
410 1.1.1.3 christos $NTPCONF
411 1.1.1.3 christos -h show help
412 1.1.1.3 christos -i Specify number of minutes between retries
413 1.1.1.3 christos $INTERVAL
414 1.1.1.3 christos -L Absolute path to leapfile on the local system
415 1.1.1.3 christos (overrides value in ntp.conf)
416 1.1.1.3 christos -l Specify the syslog(3) facility for logging
417 1.1.1.3 christos $LOGFAC
418 1.1.1.3 christos -q Only report errors (cannot be used with -v)
419 1.1.1.3 christos -r Specify number of attempts to retrieve file
420 1.1.1.3 christos $MAXTRIES
421 1.1.1.3 christos -s Send output to syslog(3) - implied if STDOUT has no tty or redirected
422 1.1.1.3 christos -t Send output to terminal - implied if STDOUT attached to terminal
423 1.1.1.3 christos -u Specify the URL of the master copy to download
424 1.1.1.3 christos $LEAPSRC
425 1.1.1.3 christos -v Verbose - show debug messages (cannot be used with -q)
426 1.1 christos
427 1.1.1.3 christos The following options are not (yet) implemented in the perl version:
428 1.1.1.3 christos -4 Use only IPv4
429 1.1.1.3 christos -6 Use only IPv6
430 1.1.1.3 christos -c Command to restart NTP after installing a new file
431 1.1.1.3 christos <none> - ntpd checks file daily
432 1.1.1.3 christos -p 4|6
433 1.1.1.3 christos Prefer IPv4 or IPv6 (as specified) addresses, but use either
434 1.1 christos
435 1.1.1.3 christos $PROG will validate the file currently on the local system.
436 1.1 christos
437 1.1.1.3 christos Ordinarily, the leapfile is found using the 'leapfile' directive in
438 1.1.1.3 christos $NTPCONF. However, an alternate location can be specified on the
439 1.1.1.3 christos command line with the -L flag.
440 1.1.1.3 christos
441 1.1.1.3 christos If the leapfile does not exist, is not valid, has expired, or is
442 1.1.1.3 christos expiring soon, a new copy will be downloaded. If the new copy is
443 1.1.1.3 christos valid, it is installed.
444 1.1 christos
445 1.1.1.3 christos If the current file is acceptable, no download or restart occurs.
446 1.1 christos
447 1.1.1.3 christos This can be run as a cron job. As the file is rarely updated, and
448 1.1.1.3 christos leap seconds are announced at least one month in advance (usually
449 1.1.1.3 christos longer), it need not be run more frequently than about once every
450 1.1.1.3 christos three weeks.
451 1.1.1.3 christos
452 1.1.1.3 christos SSL/TLS Considerations
453 1.1.1.3 christos -----------------------
454 1.1.1.3 christos The perl modules can usually locate the CA certificate used to verify
455 1.1.1.3 christos the peer's identity.
456 1.1.1.3 christos
457 1.1.1.3 christos On BSDs, the default is typically the file /etc/ssl/certs.pem. On
458 1.1.1.3 christos Linux, the location is typically a path to a CAdir - a directory of
459 1.1.1.3 christos symlinks named according to a hash of the certificates' subject names.
460 1.1.1.3 christos
461 1.1.1.3 christos The -C or -D options are available to pass in a location if no CA cert
462 1.1.1.3 christos is found in the default location.
463 1.1.1.3 christos
464 1.1.1.3 christos External Dependencies
465 1.1.1.3 christos ---------------------
466 1.1.1.3 christos The following perl modules are required:
467 1.1.1.3 christos HTTP::Tiny - version >= 0.056
468 1.1.1.3 christos IO::Socket::SSL - version >= 1.56
469 1.1.1.3 christos NET::SSLeay - version >= 1.49
470 1.1 christos
471 1.1.1.3 christos Version: $VERSION
472 1.1 christos
473 1.1.1.3 christos EOF
474 1.1 christos }
475 1.1 christos
476