From 83d2b83af9953ecbcc5917d935f077e7dabe8e10 Mon Sep 17 00:00:00 2001 From: jim-p Date: Thu, 11 May 2017 16:38:53 -0400 Subject: [PATCH] Allow a wider range of characters to be used in certificate fields, as laid out by RFC 4514. Fixes #7540 --- src/etc/inc/certs.inc | 16 +++++++++ src/usr/local/www/system_camanager.php | 32 +++++++---------- src/usr/local/www/system_certmanager.php | 46 +++++++++++------------- 3 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/etc/inc/certs.inc b/src/etc/inc/certs.inc index cb3d1b1e0f..b30a6071f2 100644 --- a/src/etc/inc/certs.inc +++ b/src/etc/inc/certs.inc @@ -995,4 +995,20 @@ function cert_get_cn($crt, $isref = false) { return ""; } +function cert_escape_x509_chars($str, $reverse = false) { + /* Characters which need escaped when present in x.509 fields. + * See https://www.ietf.org/rfc/rfc4514.txt + * + * The backslash (\) must be listed first in these arrays! + */ + $cert_directory_string_special_chars = array('\\', '"', '#', '+', ',', ';', '<', '=', '>'); + $cert_directory_string_special_chars_esc = array('\\\\', '\"', '\#', '\+', '\,', '\;', '\<', '\=', '\>'); + if ($reverse) { + return str_replace($cert_directory_string_special_chars_esc, $cert_directory_string_special_chars, $str); + } else { + /* First unescape and then escape again, to prevent possible double escaping. */ + return str_replace($cert_directory_string_special_chars, $cert_directory_string_special_chars_esc, cert_escape_x509_chars($str, true)); + } +} + ?> diff --git a/src/usr/local/www/system_camanager.php b/src/usr/local/www/system_camanager.php index 550de3ef6a..460d58e894 100644 --- a/src/usr/local/www/system_camanager.php +++ b/src/usr/local/www/system_camanager.php @@ -220,12 +220,6 @@ if ($_POST['save']) { if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_email"])) { array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters.")); } - } else if ($reqdfields[$i] == 'dn_commonname') { - if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_commonname"])) { - array_push($input_errors, gettext("The field 'Distinguished name Common Name' contains invalid characters.")); - } - } else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST["$reqdfields[$i]"])) { - array_push($input_errors, sprintf(gettext("The field '%s' contains invalid characters."), $reqdfieldsn[$i])); } } if (!in_array($_POST["keylen"], $ca_keylens)) { @@ -266,13 +260,13 @@ if ($_POST['save']) { } else if ($pconfig['method'] == "internal") { $dn = array( 'countryName' => $pconfig['dn_country'], - 'stateOrProvinceName' => $pconfig['dn_state'], - 'localityName' => $pconfig['dn_city'], - 'organizationName' => $pconfig['dn_organization'], - 'emailAddress' => $pconfig['dn_email'], - 'commonName' => $pconfig['dn_commonname']); + 'stateOrProvinceName' => cert_escape_x509_chars($pconfig['dn_state']), + 'localityName' => cert_escape_x509_chars($pconfig['dn_city']), + 'organizationName' => cert_escape_x509_chars($pconfig['dn_organization']), + 'emailAddress' => cert_escape_x509_chars($pconfig['dn_email']), + 'commonName' => cert_escape_x509_chars($pconfig['dn_commonname'])); if (!empty($pconfig['dn_organizationalunit'])) { - $dn['organizationalUnitName'] = $pconfig['dn_organizationalunit']; + $dn['organizationalUnitName'] = cert_escape_x509_chars($pconfig['dn_organizationalunit']); } if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'])) { $input_errors = array(); @@ -285,13 +279,13 @@ if ($_POST['save']) { } else if ($pconfig['method'] == "intermediate") { $dn = array( 'countryName' => $pconfig['dn_country'], - 'stateOrProvinceName' => $pconfig['dn_state'], - 'localityName' => $pconfig['dn_city'], - 'organizationName' => $pconfig['dn_organization'], - 'emailAddress' => $pconfig['dn_email'], - 'commonName' => $pconfig['dn_commonname']); + 'stateOrProvinceName' => cert_escape_x509_chars($pconfig['dn_state']), + 'localityName' => cert_escape_x509_chars($pconfig['dn_city']), + 'organizationName' => cert_escape_x509_chars($pconfig['dn_organization']), + 'emailAddress' => cert_escape_x509_chars($pconfig['dn_email']), + 'commonName' => cert_escape_x509_chars($pconfig['dn_commonname'])); if (!empty($pconfig['dn_organizationalunit'])) { - $dn['organizationalUnitName'] = $pconfig['dn_organizationalunit']; + $dn['organizationalUnitName'] = cert_escape_x509_chars($pconfig['dn_organizationalunit']); } if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'])) { $input_errors = array(); @@ -387,7 +381,7 @@ foreach ($a_ca as $i => $ca): } else { $issuer_name = gettext("external"); } - $subj = htmlspecialchars($subj); + $subj = htmlspecialchars(cert_escape_x509_chars($subj, true)); $issuer = htmlspecialchars($issuer); $certcount = 0; diff --git a/src/usr/local/www/system_certmanager.php b/src/usr/local/www/system_certmanager.php index 1352407b3a..34fc326c29 100644 --- a/src/usr/local/www/system_certmanager.php +++ b/src/usr/local/www/system_certmanager.php @@ -351,12 +351,6 @@ if ($_POST['save']) { if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) { array_push($input_errors, gettext("The field 'Distinguished name Email Address' contains invalid characters.")); } - } else if (preg_match('/commonname/', $reqdfields[$i])) { /* dn_commonname or csr_dn_commonname */ - if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) { - array_push($input_errors, gettext("The field 'Distinguished name Common Name' contains invalid characters.")); - } - } else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST[$reqdfields[$i]])) { - array_push($input_errors, sprintf(gettext("The field '%s' contains invalid characters."), $reqdfieldsn[$i])); } } @@ -443,13 +437,13 @@ if ($_POST['save']) { if ($pconfig['method'] == "internal") { $dn = array( 'countryName' => $pconfig['dn_country'], - 'stateOrProvinceName' => $pconfig['dn_state'], - 'localityName' => $pconfig['dn_city'], - 'organizationName' => $pconfig['dn_organization'], - 'emailAddress' => $pconfig['dn_email'], - 'commonName' => $pconfig['dn_commonname']); + 'stateOrProvinceName' => cert_escape_x509_chars($pconfig['dn_state']), + 'localityName' => cert_escape_x509_chars($pconfig['dn_city']), + 'organizationName' => cert_escape_x509_chars($pconfig['dn_organization']), + 'emailAddress' => cert_escape_x509_chars($pconfig['dn_email']), + 'commonName' => cert_escape_x509_chars($pconfig['dn_commonname'])); if (!empty($pconfig['dn_organizationalunit'])) { - $dn['organizationalUnitName'] = $pconfig['dn_organizationalunit']; + $dn['organizationalUnitName'] = cert_escape_x509_chars($pconfig['dn_organizationalunit']); } if (is_ipaddr($pconfig['dn_commonname'])) { $altnames_tmp = array("IP:{$pconfig['dn_commonname']}"); @@ -481,13 +475,13 @@ if ($_POST['save']) { if ($pconfig['method'] == "external") { $dn = array( 'countryName' => $pconfig['csr_dn_country'], - 'stateOrProvinceName' => $pconfig['csr_dn_state'], - 'localityName' => $pconfig['csr_dn_city'], - 'organizationName' => $pconfig['csr_dn_organization'], - 'emailAddress' => $pconfig['csr_dn_email'], - 'commonName' => $pconfig['csr_dn_commonname']); + 'stateOrProvinceName' => cert_escape_x509_chars($pconfig['csr_dn_state']), + 'localityName' => cert_escape_x509_chars($pconfig['csr_dn_city']), + 'organizationName' => cert_escape_x509_chars($pconfig['csr_dn_organization']), + 'emailAddress' => cert_escape_x509_chars($pconfig['csr_dn_email']), + 'commonName' => cert_escape_x509_chars($pconfig['csr_dn_commonname'])); if (!empty($pconfig['csr_dn_organizationalunit'])) { - $dn['organizationalUnitName'] = $pconfig['csr_dn_organizationalunit']; + $dn['organizationalUnitName'] = cert_escape_x509_chars($pconfig['csr_dn_organizationalunit']); } if (count($altnames)) { $altnames_tmp = ""; @@ -1119,7 +1113,7 @@ foreach ($a_cert as $i => $cert): $caname = ''. gettext("external").''; } - $subj = htmlspecialchars($subj); + $subj = htmlspecialchars(cert_escape_x509_chars($subj, true)); } else { $subj = ""; $issuer = ""; @@ -1130,7 +1124,7 @@ foreach ($a_cert as $i => $cert): } if ($cert['csr']) { - $subj = htmlspecialchars(csr_get_subject($cert['csr'])); + $subj = htmlspecialchars(cert_escape_x509_chars(csr_get_subject($cert['csr']), true)); $sans = cert_get_sans($cert['crt']); $caname = "" . gettext("external - signature pending") . ""; } @@ -1266,12 +1260,12 @@ events.push(function() { $subject = cert_get_subject_array($ca['crt']); ?> case "": - $('#dn_country').val(""); - $('#dn_state').val(""); - $('#dn_city').val(""); - $('#dn_organization').val(""); - $('#dn_email').val(""); - $('#dn_organizationalunit').val(""); + $('#dn_country').val(); + $('#dn_state').val(); + $('#dn_city').val(); + $('#dn_organization').val(); + $('#dn_email').val(); + $('#dn_organizationalunit').val(); break;