apache2-mod_nss/mod_nss_migrate.pl
Cristian Rodríguez d206ad095d Accepting request 375069 from home:vitezslav_cizek:branches:Apache:Modules
- use a whitelist approach for keeping directives in the migration
  script (bsc#961907)
  * modify mod_nss_migrate.pl

- fix test: add NSSPassPhraseDialog, point it to plain file

- update to 1.0.13
  Update default ciphers to something more modern and secure
  Check for host and netstat commands in gencert before trying to use them
  Add server support for DHE ciphers
  Extract SAN from server/client certificates into env
  Fix memory leaks and other coding issues caught by clang analyzer
  Add support for Server Name Indication (SNI) (#1010751)
  Add support for SNI for reverse proxy connections
  Add RenegBufferSize? option
  Add support for TLS Session Tickets (RFC 5077)
  Fix logical AND support in OpenSSL cipher compatibility
  Correctly handle disabled ciphers (CVE-2015-5244)
  Implement a slew more OpenSSL cipher macros
  Fix a number of illegal memory accesses and memory leaks
  Support for SHA384 ciphers if they are available in NSS
  Add compatibility for mod_ssl-style cipher definitions (#862938)
  Add TLSv1.2-specific ciphers
  Completely remove support for SSLv2
  Add support for sqlite NSS databases (#1057650)
  Compare subject CN and VS hostname during server start up
  Add support for enabling TLS v1.2
  Don't enable SSL 3 by default (CVE-2014-3566)
  Fix CVE-2013-4566
  Move nss_pcache to /usr/libexec

OBS-URL: https://build.opensuse.org/request/show/375069
OBS-URL: https://build.opensuse.org/package/show/Apache:Modules/apache2-mod_nss?expand=0&rev=22
2016-03-30 14:57:58 +00:00

402 lines
13 KiB
Perl

#!/usr/bin/perl
#
# Migrate configuration from OpenSSL to NSS
use Cwd;
use Getopt::Std;
BEGIN {
#$NSSDir = cwd();
$NSSDir = "/etc/apache2/mod_nss.d";
$SSLCACertificatePath = "";
$SSLCACertificateFile = "";
$SSLCertificateFile = "";
$SSLCARevocationPath = "";
$SSLCARevocationFile = "";
$SSLCertificateKeyFile = "";
$passphrase = 0;
}
# these directives are common for mod_ssl 2.4.18 and mod_nss 1.0.13
%keep = ( "SSLCipherSuite" => "",
"SSLEngine" => "",
"SSLFIPS" => "",
"SSLOptions" => "",
"SSLPassPhraseDialog" => "",
"SSLProtocol" => "",
"SSLProxyCipherSuite" => "",
"SSLProxyEngine" => "",
"SSLProxyCheckPeerCN" => "",
"SSLProxyProtocol" => "",
"SSLRandomSeed" => "",
"SSLRenegBufferSize" => "",
"SSLRequire" => "",
"SSLRequireSSL" => "",
"SSLSessionCacheTimeout" => "",
"SSLSessionTickets" => "",
"SSLStrictSNIVHostCheck" => "",
"SSLUserName" => "",
"SSLVerifyClient" => "",
);
%insert = ( "SSLSessionCacheTimeout", "NSSSessionCacheSize 10000\nNSSSession3CacheTimeout 86400\n",);
getopts('chr:w:' , \%opt );
sub usage() {
print STDERR "Usage: migrate.pl [-c] -r <mod_ssl input file> -w <mod_nss output file>\n";
print STDERR "\t-c converts the certificates\n";
print STDERR "This conversion script is not aware of apache's configuration blocks\n";
print STDERR "and nestable conditional directives. Please check the output of the\n";
print STDERR "conversion and adjust manually if necessary!\n";
exit();
}
usage() if ($opt{h} || !$opt{r} || !$opt{w});
print STDERR "input: $opt{r} output: $opt{w}\n";
open (SSL, "<", $opt{r} ) or die "Unable to open $opt{r}: $!.\n";
open (NSS, ">", $opt{w} ) or die "Unable to open $opt{w}: $!.\n";
print NSS "## This is a conversion of mod_ssl specific options by migrate.pl\n";
print NSS "## Most of the comments in the original .conf file have been omitted here, as\n";
print NSS "## the comments may not be valid for mod_nss, too.\n";
print NSS "## \n";
print NSS "## Please read through this configuration and verify the individual options!\n\n";
while (<SSL>) {
my $comment = 0;
# write through even if in comment before comments are stripped below.
if(/(ServerName|ServerAlias)/) {
print NSS $_;
next;
}
# skip blank lines and comments
if (/^\s*#/ || /^\s*$/) {
print NSS $_;
next;
}
s/mod_ssl\.c/mod_nss.c/;
# write through nestable apache configuration block directives:
if (/^</ || /^\s</) {
print NSS $_;
next;
}
m/(\w+)\s+(.+)/;
$stmt = $1;
$value = $2;
# Handle the special cases
if ($stmt eq "SSLVerifyClient" && $value eq "optional_no_ca") {
print NSS "# Replaced optional_no_ca with optional\n";
print NSS "SSLVerifyClient optional\n";
next;
}
# we support OpenSSL cipher strings now, keeping the string as is
#if ($stmt eq "SSLCipherSuite") {
#print NSS "NSSCipherSuite ", get_ciphers($val), "\n";
#print NSS "NSSProtocol SSLv3,TLSv1\n";
#$comment = 1;
if ($stmt eq "SSLProtocol" ) {
print NSS "## we ignore the arguments to SSLProtocol. The original value was:\n";
print NSS "##$_";
print NSS "## The following is a _range_ from TLSv1.0 to TLSv1.2.\n";
print NSS "NSSProtocol TLSv1.0,TLSv1.2\n\n";
next;
} elsif ($stmt eq "SSLCACertificatePath") {
$SSLCACertificatePath = $value;
$comment = 1;
} elsif ($stmt eq "SSLCACertificateFile") {
$SSLCACertificateFile = $value;
$comment = 1;
} elsif ($stmt eq "SSLCertificateFile") {
print NSS "NSSCertificateDatabase $NSSDir\n";
print NSS "NSSNickName Server-Cert\n";
$SSLCertificateFile = $value;
$comment = 1;
} elsif ($stmt eq "SSLCertificateKeyFile") {
$SSLCertificateKeyFile = $value;
$comment = 1;
} elsif ($stmt eq "SSLCARevocationPath") {
$SSLCARevocationPath = $value;
$comment = 1;
} elsif ($stmt eq "SSLCARevocationFile") {
$SSLCARevocationFile = $value;
$comment = 1;
} elsif ($stmt eq "SSLPassPhraseDialog") {
print NSS "NSSPassPhraseHelper /usr/libexec/nss_pcache\n";
$passphrase = 1;
$comment = 1;
}
if (exists($insert{$stmt})) {
#print NSS "$_";
print NSS $insert{$stmt};
next;
}
if (m/^\s*SSL/) {
if (!exists($keep{$stmt})) {
print NSS "# Skipping, not applicable in mod_nss\n";
print NSS "##$_";
next;
} else {
# Fix up any remaining directive names
s/^(\s*)SSL/\1NSS/;
}
}
# Fall-through to print whatever is left
if ($comment) {
print NSS "##$_";
$comment = 0;
} else {
print NSS $_;
}
}
if ($passphrase == 0) {
# NOTE: Located at '/usr/sbin/nss_pcache' prior to 'mod_nss-1.0.9'.
print NSS "NSSPassPhraseHelper /usr/libexec/nss_pcache\n";
}
close(NSS);
close(SSL);
#
# Create NSS certificate database and import any existing certificates
#
if ($opt{c}) {
print STDERR "Creating NSS certificate database.\n";
run_command("certutil -N -d $NSSDir");
# Convert the certificate into pkcs12 format
if ($SSLCertificateFile ne "" && $SSLCertificateKeyFile ne "") {
my $subject = get_cert_subject($SSLCertificateFile);
print STDERR "Importing certificate $subject as \"Server-Cert\".\n";
run_command("openssl pkcs12 -export -in $SSLCertificateFile -inkey $SSLCertificateKeyFile -out server.p12 -name \"Server-Cert\" -passout pass:foo");
run_command("pk12util -i server.p12 -d $NSSDir -W foo");
}
if ($SSLCACertificateFile ne "") {
my $subject = get_cert_subject($SSLCACertificateFile);
if ($subject ne "") {
print STDERR "Importing CA certificate $subject\n";
run_command("certutil -A -n \"$subject\" -t \"CT,,\" -d $NSSDir -a -i $SSLCACertificateFile");
}
}
if ($SSLCACertificatePath ne "") {
opendir(DIR, $SSLCACertificatePath) or die "can't opendir $SSLCACertificatePath: $!";
while (defined($file = readdir(DIR))) {
next if -d $file;
# we can operate directly on the hash files so don't have to worry
# about any SKIPME's.
if ($file =~ /hash.*/) {
my $subject = get_cert_subject("$SSLCACertificatePath/$file");
if ($subject ne "") {
print STDERR "Importing CA certificate $subject\n";
run_command("certutil -A -n \"$subject\" -t \"CT,,\" -d $NSSDir -a -i $SSLCACertificatePath/$file");
}
}
}
closedir(DIR);
}
if ($SSLCARevocationFile ne "") {
print STDERR "Importing CRL file $CARevocationFile\n";
# Convert to DER format
run_command("openssl crl -in $SSLCARevocationFile -out /root/crl.tmp -inform PEM -outform DER");
run_command("crlutil -I -t 1 -d $NSSDir -i /root/crl.tmp");
unlink("/root/crl.tmp");
}
if ($SSLCARevocationPath ne "") {
opendir(DIR, $SSLCARevocationPath) or die "can't opendir $SSLCARevocationPath: $!";
while (defined($file = readdir(DIR))) {
next if -d $file;
# we can operate directly on the hash files so don't have to worry
# about any SKIPME's.
if ($file =~ /hash.*/) {
my $subject = get_cert_subject("$SSLCARevocationPath/$file");
if ($subject ne "") {
print STDERR "Importing CRL file $file\n";
# Convert to DER format
run_command("openssl crl -in $SSLCARevocationPath/$file -out /root/crl.tmp -inform PEM -outform DER");
run_command("crlutil -I -t 1 -d $NSSDir -i /root/crl.tmp");
unlink("/root/crl.tmp");
}
}
}
closedir(DIR);
}
}
print STDERR "\n\nConversion complete.\n";
print STDERR "The output file should contain a valid mod_nss configuration based on\n";
print STDERR "the mod_ssl directives from the input file.\n";
print STDERR "Recommended directory: /etc/apache2/mod_nss.d , suffix .conf!\n";
print STDERR "Also make sure to edit /etc/apache2/conf.d/mod_nss.conf and to remove the\n";
print STDERR "<VirtualHost> section if you do not need it.\n\n";
print STDERR "Also, do not forget to rename the ssl based apache config file";
print STDERR "(our example: myhost-ssl.conf) to a file that does not end in .conf\n";
print STDERR "(our example: myhost-ssl.conf-disabled-for-nss)\n\n";
print STDERR "Then, restart apache (rcapache2 restart) and have a look into the error logs.\n";
exit(0);
# Migrate configuration from OpenSSL to NSS
sub get_ciphers {
my $str = shift;
%cipher_list = (
"rc4" => ":ALL:SSLv2:RSA:MD5:MEDIUM:RC4:",
"rc4export" => ":ALL:SSLv2:RSA:EXP:EXPORT40:MD5:RC4:",
"rc2" => ":ALL:SSLv2:RSA:MD5:MEDIUM:RC2:",
"rc2export" => ":ALL:SSLv2:RSA:EXP:EXPORT40:MD5:RC2:",
"des" => ":ALL:SSLv2:RSA:EXP:EXPORT56:MD5:DES:LOW:",
"desede3" => ":ALL:SSLv2:RSA:MD5:3DES:HIGH:",
"rsa_rc4_128_md5" => ":ALL:SSLv3:TLSv1:RSA:MD5:RC4:MEDIUM:",
"rsa_rc4_128_sha" => ":ALL:SSLv3:TLSv1:RSA:SHA:RC4:MEDIUM:",
"rsa_3des_sha" => ":ALL:SSLv3:TLSv1:RSA:SHA:3DES:HIGH:",
"rsa_des_sha" => ":ALL:SSLv3:TLSv1:RSA:SHA:DES:LOW:",
"rsa_rc4_40_md5" => ":ALL:SSLv3:TLSv1:RSA:EXP:EXPORT40:RC4:",
"rsa_rc2_40_md5" => ":ALL:SSLv3:TLSv1:RSA:EXP:EXPORT40:RC2:",
"rsa_null_md5" => ":SSLv3:TLSv1:RSA:MD5:NULL:",
"rsa_null_sha" => ":SSLv3:TLSv1:RSA:SHA:NULL:",
"rsa_des_56_sha" => ":ALL:SSLv3:TLSv1:RSA:DES:SHA:EXP:EXPORT56:",
"rsa_rc4_56_sha" => ":ALL:SSLv3:TLSv1:RSA:RC4:SHA:EXP:EXPORT56:",
);
$NUM_CIPHERS = 16;
for ($i = 0; $i < $NUM_CIPHERS; $i++) {
$selected[$i] = 0;
}
# Don't need to worry about the ordering properties of "+" because
# NSS always chooses the "best" cipher anyway. You can't specify
# preferred order.
# -1: this cipher is completely out
# 0: this cipher is currently unselected, but maybe added later
# 1: this cipher is selected
@s = split(/:/, $str);
for ($i = 0; $i <= $#s; $i++) {
$j = 0;
$val = 1;
# ! means this cipher is disabled forever
if ($s[$i] =~ /^!/) {
$val = -1;
($s[$i] =~ s/^!//);
} elsif ($s[$i] =~ /^-/) {
$val = 0;
($s[$i] =~ s/^-//);
} elsif ($s[$i] =~ /^+/) {
($s[$i] =~ s/^+//);
}
for $cipher (sort keys %cipher_list) {
$match = 0;
# For embedded + we do an AND for all options
if ($s[$i] =~ m/(\w+\+)+/) {
@sub = split(/^\+/, $s[$i]);
$match = 1;
for ($k = 0; $k <=$#sub; $k++) {
if ($cipher_list{$cipher} !=~ m/:$sub[$k]:/) {
$match = 0;
}
}
} else { # straightforward match
if ($cipher_list{$cipher} =~ m/:$s[$i]:/) {
$match = 1;
}
}
if ($match && $selected[$j] != -1) {
$selected[$j] = $val;
}
$j++;
}
}
# NSS doesn't honor the order of a cipher list, it uses the "strongest"
# cipher available. So we'll print out the ciphers as SSLv2, SSLv3 and
# the NSS ciphers not available in OpenSSL.
$str = "SSLv2:SSLv3";
@s = split(/:/, $str);
$ciphersuite = "";
for ($i = 0; $i <= $#s; $i++) {
$j = 0;
for $cipher (sort keys %cipher_list) {
if ($cipher_list{$cipher} =~ m/:$s[$i]:/) {
if ($selected[$j]) {
$ciphersuite .= "+";
} else {
$ciphersuite .= "-";
}
$ciphersuite .= $cipher . ",";
}
$j++;
}
}
$ciphersuite .= "-fortezza,-fortezza_rc4_128_sha,-fortezza_null,-fips_des_sha,+fips_3des_sha,-rsa_aes_128_sha,-rsa_aes_256_sha";
return $ciphersuite;
}
# Given the filename of a PEM file, use openssl to fetch the certificate
# subject
sub get_cert_subject {
my $file = shift;
my $subject = "";
return "" if ! -T $file;
$subject = `openssl x509 -subject < $file | head -1`;
$subject =~ s/subject= \///; # Remove leading subject= \
$subject =~ s/\//,/g; # Replace / with , as separator
$subject =~ s/Email=.*(,){0,1}//; # Remove Email attribute
$subject =~ s/,$//; # Remove any trailing commas
chomp($subject);
return $subject;
}
#
# Wrapper around the system() command
sub run_command {
my @args = shift;
my $status = 0;
$status = 0xffff & system(@args);
return if ($status == 0);
print STDERR "Command '@args' failed: $!\n";
exit;
}