#!/usr/bin/perl
=head1 install-cert.pl
Replace the SSL certificate or private key for a virtual server.
This command is typically used to install a signed certificate that you
have received from a CA in response to a signing request, generated with
C<generate-cert>. However, it can be used to install any certificate,
private key or CA certificate file into a virtual server.
The server must be specified with the C<--domain> flag, followed by a domain
name. When installing a signed cert, you should use the C<--cert> flag
followed by the path to the certificate file, which will be copied into the
virtual server's home directory. You should also use the C<--use-newkey> flag
to use the key generated at the same time as the CSR.
Alternately, you can install a new matching key and certificate with the
C<--key> and C<--cert> flags. If the key is protected by a passphrase, it
must be specified with the C<--pass> parameter. Any errors in the key or
certificate format or the match between them will cause the command to fail
before the web server configuration is updated.
=cut
package virtual_server;
if (!$module_name) {
$main::no_acl_check++;
$ENV{'WEBMIN_CONFIG'} ||= "/etc/webmin";
$ENV{'WEBMIN_VAR'} ||= "/var/webmin";
if ($0 =~ /^(.*)\/[^\/]+$/) {
chdir($pwd = $1);
}
else {
chop($pwd = `pwd`);
}
$0 = "$pwd/install-cert.pl";
require './virtual-server-lib.pl';
$< == 0 || die "install-cert.pl must be run as root";
}
@OLDARGV = @ARGV;
&set_all_text_print();
# Parse command-line args
while(@ARGV > 0) {
local $a = shift(@ARGV);
if ($a eq "--domain") {
$dname = shift(@ARGV);
}
elsif ($a eq "--cert" || $a eq "--key" ||
$a eq "--csr" || $a eq "--ca") {
$g = substr($a, 2);
$f = shift(@ARGV);
if ($f =~ /^\//) {
# In some file on the server
$data = &read_file_contents($f);
$data || &usage("File $f does not exist");
push(@got, [ $g, $data ]);
}
else {
# In parameter
$f =~ s/\r//g;
$f =~ s/\s+/\n/g;
push(@got, [ $g, $f ]);
}
}
elsif ($a eq "--use-newkey") {
$usenewkey = 1;
}
elsif ($a eq "--pass") {
$newpass = shift(@ARGV);
}
elsif ($a eq "--multiline") {
$multiline = 1;
}
else {
&usage("Unknown parameter $a");
}
}
$dname || &usage("Missing --domain parameter");
@got || &usage("No new certificates or keys given");
$d = &get_domain_by("dom", $dname);
$d || &usage("No virtual server named $dname found");
&domain_has_ssl($d) ||
&usage("Virtual server $dname does not have SSL enabled");
if ($usenewkey) {
($clash) = grep { $_->[0] eq 'key' } @got;
$clash && &usage("--use-newkey and --key cannot both be given");
$d->{'ssl_newkey'} || &usage("--use-newkey can only be given if a ".
"private key and CSR have been created");
$newkey = &read_file_contents($d->{'ssl_newkey'});
$newkey || &usage("Private key matching CSR was not found");
push(@got, [ 'key', $newkey ]);
}
# Validate given certs and keys for basic formatting
foreach $g (@got) {
$err = &validate_cert_format($g->[1], $g->[0]);
$err && &usage("Invalid data for $g->[0] : $err");
}
# Make sure new cert and key will match
$checkcert = &read_file_contents($d->{'ssl_cert'});
$checkkey = &read_file_contents($d->{'ssl_key'});
foreach $g (@got) {
if ($g->[0] eq 'cert') {
$checkcert = $g->[1];
}
elsif ($g->[0] eq 'key') {
$checkkey = $g->[1];
}
}
$passok = &check_passphrase($checkkey, $d->{'ssl_pass'} || $newpass);
$passok || &usage("Private key is password-protected, but either none was entered or the password was incorrect");
$err = &check_cert_key_match($checkcert, $checkkey);
$err && &usage("Certificate problems found : $err");
# Break SSL linkages that no longer work with this cert
($gotcert) = grep { $_->[0] eq 'cert' } @got;
if ($gotcert) {
$temp = &transname();
&open_tempfile(TEMP, ">$temp", 0, 1);
&print_tempfile(TEMP, $gotcert->[1]);
&close_tempfile(TEMP);
$newcertinfo = &cert_file_info($temp);
&break_invalid_ssl_linkages($d, $newcertinfo);
&unlink_file($temp);
}
&$first_print("Installing new SSL files ..");
$changed = 0;
foreach $g (@got) {
$d->{'ssl_'.$g->[0]} ||= &default_certificate_file($d, $g->[0]);
&lock_file($d->{'ssl_'.$g->[0]});
&open_tempfile_as_domain_user($d, SSL, ">".$d->{'ssl_'.$g->[0]});
&print_tempfile(SSL, $g->[1]);
&close_tempfile_as_domain_user($d, SSL);
&set_certificate_permissions($d, $d->{'ssl_'.$g->[0]});
&unlock_file($d->{'ssl_'.$g->[0]});
if ($g->[0] ne 'csr') {
&save_website_ssl_file($d, $g->[0], $d->{'ssl_'.$g->[0]});
}
}
&$second_print(".. done");
# Remove old private key and CSR, as they are now installed
if ($usenewkey) {
&unlink_logged($d->{'ssl_newkey'});
delete($d->{'ssl_newkey'});
delete($d->{'ssl_csr'});
}
# If a passphrase is needed, add it to the top-level Apache config. This is
# done by creating a small script that outputs the passphrase
$d->{'ssl_pass'} = $passok == 2 ? $newpass : undef;
&save_domain_passphrase($d);
&save_domain($d);
# Copy SSL directives to domains using same cert
foreach $od (&get_domain_by("ssl_same", $d->{'id'})) {
$od->{'ssl_cert'} = $d->{'ssl_cert'};
$od->{'ssl_key'} = $d->{'ssl_key'};
$od->{'ssl_newkey'} = $d->{'ssl_newkey'};
$od->{'ssl_csr'} = $d->{'ssl_csr'};
$od->{'ssl_pass'} = $d->{'ssl_pass'};
&save_domain_passphrase($od);
}
&run_post_actions();
&virtualmin_api_log(\@OLDARGV, $d);
sub usage
{
print "$_[0]\n\n" if ($_[0]);
print "Installs a certificate, private key, CSR or CA certificate.\n";
print "\n";
print "virtualmin install-cert --domain name\n";
print " [--cert file|data]\n";
print " [--key file|data]\n";
print " [--ca file|data]\n";
print " [--csr file|data]\n";
print " [--use-newkey]\n";
print " [--pass key-password]\n";
exit(1);
}