URL: https://github.com/freeipa/freeipa/pull/834
Author: tomaskrizek
 Title: #834: [4.4] NSSNickname enclosed in single quotes causes 
ipa-server-certinstall failure
Action: opened

PR body:
"""
Original PR: https://github.com/freeipa/freeipa/pull/347

https://pagure.io/freeipa/issue/6991
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/834/head:pr834
git checkout pr834
From 9a19c4c157631af6e2414b27776316ba45aef27d Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 16 Dec 2016 12:14:20 +0100
Subject: [PATCH 1/4] Fix the installutils.set_directive docstring

Add missing parameter descriptions and fix incorrect indentation

https://fedorahosted.org/freeipa/ticket/6991
---
 ipaserver/install/installutils.py | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 07e438d6ca..51528a1d0a 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -384,11 +384,14 @@ def set_directive(filename, directive, value, quotes=True, separator=' ',
 
     This has only been tested with nss.conf
 
-   :param directive: directive name
-   :param value: value of the directive
-   :param quotes: whether to quote `value` in `quote_char`. If true, then
-        the `quote_char` are first escaped to avoid unparseable directives
-   :param quote_char: the character used for quoting `value`
+    :param filename: input filename
+    :param directive: directive name
+    :param value: value of the directive
+    :param quotes: whether to quote `value` in `quote_char`. If true, then
+        the `quote_char` are first escaped to avoid unparseable directives.
+    :param separator: character serving as separator between directive and
+        value
+    :param quote_char: the character used for quoting `value`
     """
 
     def format_directive(directive, value, separator, quotes, quote_char):

From 9cca3455dffcf81a63c9d8577652276cc24ad424 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 16 Dec 2016 13:34:57 +0100
Subject: [PATCH 2/4] installutils: improve directive value parsing in
 `get_directive`

`get_directive` value parsing was improved in order to bring its logic
more in-line to changes in `set_directive`: a specified quoting
character is now unquoted and stripped from the retrieved value. The
function will now also error out when malformed directive is
encountered.

https://fedorahosted.org/freeipa/ticket/6991
---
 ipaserver/install/installutils.py | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 51528a1d0a..db207eb05c 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -432,16 +432,31 @@ def format_directive(directive, value, separator, quotes, quote_char):
     fd.close()
     os.chown(filename, st.st_uid, st.st_gid) # reset perms
 
+
 def get_directive(filename, directive, separator=' '):
     """
     A rather inefficient way to get a configuration directive.
+
+    :param filename: input filename
+    :param directive: directive name
+    :param separator: separator between directive and value
+    :param quote_char: the characters that are used in this particular config
+        file to quote values. This character will be stripped and unescaped
+        from the raw value.
+
+    :returns: The (unquoted) value if the directive was found, None otherwise
     """
     fd = open(filename, "r")
     for line in fd:
         if line.lstrip().startswith(directive):
             line = line.strip()
-            result = line.split(separator, 1)[1]
-            result = result.strip('"')
+
+            (directive, sep, value) = line.partition(separator)
+            if not sep or not value:
+                raise ValueError("Malformed directive: {}".format(line))
+
+            result = value.strip().strip(quote_char)
+            result = ipautil.unescape_seq(quote_char, result)[0]
             result = result.strip(' ')
             fd.close()
             return result

From 098a5244972717cc606e2c2b8e58dabc0826981b Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Tue, 10 Jan 2017 17:15:33 +0100
Subject: [PATCH 3/4] Delegate directive value quoting/unquoting to separate
 functions

Separate functions were added to installutils module to quote/unquote a
string in arbitrary characters.

`installutils.get/set_directive` functions will use them to enclose
the directive values in double quotes/strip the double quotes from
retrieved values to maintain the original behavior.

These functions can be used also for custom quoting/unquoting of
retrieved values when desired.

https://fedorahosted.org/freeipa/ticket/6991
---
 ipaserver/install/installutils.py | 70 ++++++++++++++++++++++++---------------
 1 file changed, 43 insertions(+), 27 deletions(-)

diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index db207eb05c..b1f52f28a9 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -376,8 +376,38 @@ def update_file(filename, orig, subst):
         return 1
 
 
-def set_directive(filename, directive, value, quotes=True, separator=' ',
-                  quote_char='\"'):
+def quote_directive_value(value, quote_char):
+    """Quote a directive value
+    :param value: string to quote
+    :param quote_char: character which is used for quoting. All prior
+        occurences will be escaped before quoting to avoid unparseable value.
+    :returns: processed value
+    """
+    if value.startswith(quote_char) and value.endswith(quote_char):
+        return value
+
+    return "{quote}{value}{quote}".format(
+        quote=quote_char,
+        value="".join(ipautil.escape_seq(quote_char, value))
+    )
+
+
+def unquote_directive_value(value, quote_char):
+    """Unquote a directive value
+    :param value: string to unquote
+    :param quote_char: character to strip. All escaped occurences of
+        `quote_char` will be uncescaped during processing
+    :returns: processed value
+    """
+    unescaped_value = "".join(ipautil.unescape_seq(quote_char, value))
+    if (unescaped_value.startswith(quote_char) and
+            unescaped_value.endswith(quote_char)):
+        return unescaped_value[1:-1]
+
+    return unescaped_value
+
+
+def set_directive(filename, directive, value, quotes=True, separator=' '):
     """Set a name/value pair directive in a configuration file.
 
     A value of None means to drop the directive.
@@ -387,25 +417,19 @@ def set_directive(filename, directive, value, quotes=True, separator=' ',
     :param filename: input filename
     :param directive: directive name
     :param value: value of the directive
-    :param quotes: whether to quote `value` in `quote_char`. If true, then
-        the `quote_char` are first escaped to avoid unparseable directives.
+    :param quotes: whether to quote `value` in double quotes. If true, then
+        any existing double quotes are first escaped to avoid
+        unparseable directives.
     :param separator: character serving as separator between directive and
         value
-    :param quote_char: the character used for quoting `value`
     """
 
-    def format_directive(directive, value, separator, quotes, quote_char):
-        directive_sep = "{directive}{separator}".format(directive=directive,
-                                                        separator=separator)
-        transformed_value = value
-        if quotes:
-            transformed_value = "{quote}{value}{quote}".format(
-                quote=quote_char,
-                value="".join(ipautil.escape_seq(quote_char, value))
-            )
+    new_directive_value = ""
+    if value is not None:
+        value_to_set = quote_directive_value(value, '"') if quotes else value
 
-        return "{directive_sep}{value}\n".format(
-            directive_sep=directive_sep, value=transformed_value)
+        new_directive_value = "".join(
+            [directive, separator, value_to_set, '\n'])
 
     valueset = False
     st = os.stat(filename)
@@ -415,17 +439,13 @@ def format_directive(directive, value, separator, quotes, quote_char):
         if line.lstrip().startswith(directive):
             valueset = True
             if value is not None:
-                newfile.append(
-                    format_directive(
-                        directive, value, separator, quotes, quote_char))
+                newfile.append(new_directive_value)
         else:
             newfile.append(line)
     fd.close()
     if not valueset:
         if value is not None:
-            newfile.append(
-                format_directive(
-                    directive, value, separator, quotes, quote_char))
+            newfile.append(new_directive_value)
 
     fd = open(filename, "w")
     fd.write("".join(newfile))
@@ -440,9 +460,6 @@ def get_directive(filename, directive, separator=' '):
     :param filename: input filename
     :param directive: directive name
     :param separator: separator between directive and value
-    :param quote_char: the characters that are used in this particular config
-        file to quote values. This character will be stripped and unescaped
-        from the raw value.
 
     :returns: The (unquoted) value if the directive was found, None otherwise
     """
@@ -455,8 +472,7 @@ def get_directive(filename, directive, separator=' '):
             if not sep or not value:
                 raise ValueError("Malformed directive: {}".format(line))
 
-            result = value.strip().strip(quote_char)
-            result = ipautil.unescape_seq(quote_char, result)[0]
+            result = unquote_directive_value(value.strip(), '"')
             result = result.strip(' ')
             fd.close()
             return result

From ea49d40921ad6b3119fd2eccff9af8265188d9d1 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 16 Dec 2016 13:42:05 +0100
Subject: [PATCH 4/4] Explicitly handle quoting/unquoting of NSSNickname
 directive

Improve the single/double quote handling during parsing/unparsing of
nss.conf's NSSNickname directive. Single quotes are now added/stripped
explicitly when handling the certificate nickname.

https://fedorahosted.org/freeipa/ticket/6991
---
 ipaserver/install/httpinstance.py           |  4 +++-
 ipaserver/install/ipa_server_certinstall.py | 14 +++++++++++---
 2 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 60088b0842..613303ff48 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -262,8 +262,10 @@ def __set_mod_nss_port(self):
             print("Updating port in %s failed." % paths.HTTPD_NSS_CONF)
 
     def __set_mod_nss_nickname(self, nickname):
+        quoted_nickname = installutils.quote_directive_value(
+            nickname, quote_char="'")
         installutils.set_directive(
-            paths.HTTPD_NSS_CONF, 'NSSNickname', nickname, quote_char="'")
+            paths.HTTPD_NSS_CONF, 'NSSNickname', quoted_nickname, quotes=False)
 
     def set_mod_nss_protocol(self):
         installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSProtocol', 'TLSv1.0,TLSv1.1,TLSv1.2', False)
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 7bc39e356e..16fd6e4c1f 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -140,12 +140,20 @@ def install_http_cert(self):
         old_cert = installutils.get_directive(paths.HTTPD_NSS_CONF,
                                               'NSSNickname')
 
+        unquoted_cert = installutils.unquote_directive_value(
+            old_cert, quote_char="'")
+
         server_cert = self.import_cert(dirname, self.options.pin,
-                                       old_cert, 'HTTP/%s' % api.env.host,
+                                       unquoted_cert, 'HTTP/%s' % api.env.host,
                                        'restart_httpd')
 
-        installutils.set_directive(paths.HTTPD_NSS_CONF,
-                                   'NSSNickname', server_cert)
+        quoted_server_cert = installutils.quote_directive_value(
+            server_cert, quote_char="'")
+        installutils.set_directive(
+            paths.HTTPD_NSS_CONF,
+            'NSSNickname',
+            quoted_server_cert,
+            quotes=False)
 
         # Fix the database permissions
         os.chmod(os.path.join(dirname, 'cert8.db'), 0o640)
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org

Reply via email to