On Thu, Jan 07, 2016 at 08:00:51PM +1000, Fraser Tweedale wrote:
> On Thu, Jan 07, 2016 at 07:56:15AM +0100, Jan Cholasta wrote:
> > Hi,
> > 
> > On 6.1.2016 05:26, Fraser Tweedale wrote:
> > >Happy new year, all.
> > >
> > >The attached patch fixes a unicode decode error triggered in some
> > >locales, which causes failure of installation (and probably other
> > >oprations, if locale is changed under an existing server).
> > >
> > >https://fedorahosted.org/freeipa/ticket/5578
> > 
> > It seems like this fixes only part of the issue - the installer won't crash
> > anymore. But what happens if the reason phrase uses characters which are not
> > in iso-8859-1 (e.g. "č", a character commonly used in Czech)? Shouldn't we
> > always specify the encoding in requests, so that Dogtag does not have to
> > guess?
> > 
> In this case it will not throw an exception, but it will decode
> nonsense.  However, in my investigation just now of how Tomcat
> decides what to send in the reason phrase, it turns out that in
> future releases they will not send a reason phrase[1] at all!
> 
> [1] 
> https://github.com/apache/tomcat/commit/707ab1c77f3bc189e1c3f29b641506db4c8bce37
> 
> (Nice to know about this in advance - I will not be surprised if
> some clients break)
> 
> I'll cut a new patch tomorrow that just ignores the reason phrase
> rather than trying to decode and log it.  All the info is in the
> status code, after all.
> 
> Thanks for reviewing,
> Fraser
> 
Promised new patch attached - removes the reason phrase and updates
call sites accordingly.

Cheers,
Fraser

> 
> > Honza
> > 
> > -- 
> > Jan Cholasta
> 
> -- 
> Manage your subscription for the Freeipa-devel mailing list:
> https://www.redhat.com/mailman/listinfo/freeipa-devel
> Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code
From 3d860dbb660844b76b9343d3c0908af89f0327ac Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Wed, 6 Jan 2016 14:50:42 +1100
Subject: [PATCH] Do not decode HTTP reason phrase from Dogtag

The HTTP reason phrase sent by Dogtag is assumed to be encoded in
UTF-8, but the encoding used by Tomcat is dependent on system
locale, causing decode errors in some locales.

The reason phrase is optional and will not be sent in a future
version of Tomcat[1], so do not bother decoding and returning it.

[1] 
https://github.com/apache/tomcat/commit/707ab1c77f3bc189e1c3f29b641506db4c8bce37

Fixes: https://fedorahosted.org/freeipa/ticket/5578
---
 ipapython/dogtag.py         | 23 +++++++++++------------
 ipaserver/install/certs.py  |  7 +++----
 ipaserver/plugins/dogtag.py | 44 ++++++++++++++++++++++----------------------
 3 files changed, 36 insertions(+), 38 deletions(-)

diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py
index 
010e49652687680444d18e2e8f784fb6167a0df5..1cb74719c4ce2cc97c54dc7bebfa4b32ceee14a1
 100644
--- a/ipapython/dogtag.py
+++ b/ipapython/dogtag.py
@@ -118,14 +118,14 @@ def ca_status(ca_host=None, use_proxy=True):
         ca_port = 443
     else:
         ca_port = 8443
-    status, reason, headers, body = unauthenticated_https_request(
+    status, headers, body = unauthenticated_https_request(
         ca_host, ca_port, '/ca/admin/ca/getStatus')
     if status == 503:
         # Service temporarily unavailable
-        return reason
+        return status
     elif status != 200:
         raise errors.RemoteRetrieveError(
-            reason=_("Retrieving CA status failed: %s") % reason)
+            reason=_("Retrieving CA status failed with status %d") % status)
     return _parse_ca_status(body)
 
 
@@ -136,8 +136,8 @@ def https_request(host, port, url, secdir, password, 
nickname,
     :param url: The path (not complete URL!) to post to.
     :param body: The request body (encodes kw if None)
     :param kw:  Keyword arguments to encode into POST body.
-    :return:   (http_status, http_reason_phrase, http_headers, http_body)
-               as (integer, unicode, dict, str)
+    :return:   (http_status, http_headers, http_body)
+               as (integer, dict, str)
 
     Perform a client authenticated HTTPS request
     """
@@ -165,8 +165,8 @@ def http_request(host, port, url, **kw):
     """
     :param url: The path (not complete URL!) to post to.
     :param kw: Keyword arguments to encode into POST body.
-    :return:   (http_status, http_reason_phrase, http_headers, http_body)
-                as (integer, unicode, dict, str)
+    :return:   (http_status, http_headers, http_body)
+                as (integer, dict, str)
 
     Perform an HTTP request.
     """
@@ -179,8 +179,8 @@ def unauthenticated_https_request(host, port, url, **kw):
     """
     :param url: The path (not complete URL!) to post to.
     :param kw: Keyword arguments to encode into POST body.
-    :return:   (http_status, http_reason_phrase, http_headers, http_body)
-                as (integer, unicode, dict, str)
+    :return:   (http_status, http_headers, http_body)
+                as (integer, dict, str)
 
     Perform an unauthenticated HTTPS request.
     """
@@ -219,15 +219,14 @@ def _httplib_request(
         res = conn.getresponse()
 
         http_status = res.status
-        http_reason_phrase = unicode(res.reason, 'utf-8')
         http_headers = res.msg.dict
         http_body = res.read()
         conn.close()
     except Exception as e:
         raise NetworkError(uri=uri, error=str(e))
 
-    root_logger.debug('response status %d %s', http_status, http_reason_phrase)
+    root_logger.debug('response status %d',    http_status)
     root_logger.debug('response headers %s',   http_headers)
     root_logger.debug('response body %r',      http_body)
 
-    return http_status, http_reason_phrase, http_headers, http_body
+    return http_status, http_headers, http_body
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 
1591362b4b37946aa390a8a5bfd455419382a199..f74b76090bfe2670a998373e3c7cdc3c5727c465
 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -403,12 +403,11 @@ class CertDB(object):
         result = dogtag.https_request(
             self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
             self.secdir, password, "ipaCert", **params)
-        http_status, http_reason_phrase, http_headers, http_body = result
+        http_status, http_headers, http_body = result
 
         if http_status != 200:
             raise CertificateOperationError(
-                error=_('Unable to communicate with CMS (%s)') %
-                    http_reason_phrase)
+                error=_('Unable to communicate with CMS (status %d)') % 
http_status)
 
         # The result is an XML blob. Pull the certificate out of that
         doc = xml.dom.minidom.parseString(http_body)
@@ -457,7 +456,7 @@ class CertDB(object):
         result = dogtag.https_request(
             self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
             self.secdir, password, "ipaCert", **params)
-        http_status, http_reason_phrase, http_headers, http_body = result
+        http_status, http_headers, http_body = result
         if http_status != 200:
             raise RuntimeError("Unable to submit cert request")
 
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 
1a1172a3807fe2585d15cb2363e68318e83cb09f..2549aae3d7d99364cc78f44bd96897e3738ef00d
 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -1351,8 +1351,8 @@ class ra(rabase.rabase):
         """
         :param url: The URL to post to.
         :param kw: Keyword arguments to encode into POST body.
-        :return:   (http_status, http_reason_phrase, http_headers, http_body)
-                   as (integer, unicode, dict, str)
+        :return:   (http_status, http_headers, http_body)
+                   as (integer, dict, str)
 
         Perform an HTTP request.
         """
@@ -1362,8 +1362,8 @@ class ra(rabase.rabase):
         """
         :param url: The URL to post to.
         :param kw:  Keyword arguments to encode into POST body.
-        :return:   (http_status, http_reason_phrase, http_headers, http_body)
-                   as (integer, unicode, dict, str)
+        :return:   (http_status, http_headers, http_body)
+                   as (integer, dict, str)
 
         Perform an HTTPS request
         """
@@ -1423,7 +1423,7 @@ class ra(rabase.rabase):
         self.debug('%s.check_request_status()', self.fullname)
 
         # Call CMS
-        http_status, http_reason_phrase, http_headers, http_body = \
+        http_status, http_headers, http_body = \
             self._request('/ca/ee/ca/checkRequest',
                           self.env.ca_port,
                           requestId=request_id,
@@ -1432,7 +1432,7 @@ class ra(rabase.rabase):
         # Parse and handle errors
         if http_status != 200:
             self.raise_certificate_operation_error('check_request_status',
-                                                   detail=http_reason_phrase)
+                                                   detail=http_status)
 
         parse_result = self.get_parse_result_xml(http_body, 
parse_check_request_result_xml)
         request_status = parse_result['request_status']
@@ -1508,7 +1508,7 @@ class ra(rabase.rabase):
         serial_number = int(serial_number, 0)
 
         # Call CMS
-        http_status, http_reason_phrase, http_headers, http_body = \
+        http_status, http_headers, http_body = \
             self._sslget('/ca/agent/ca/displayBySerial',
                          self.env.ca_agent_port,
                          serialNumber=str(serial_number),
@@ -1518,7 +1518,7 @@ class ra(rabase.rabase):
         # Parse and handle errors
         if http_status != 200:
             self.raise_certificate_operation_error('get_certificate',
-                                                   detail=http_reason_phrase)
+                                                   detail=http_status)
 
         parse_result = self.get_parse_result_xml(http_body, 
parse_display_cert_xml)
         request_status = parse_result['request_status']
@@ -1576,7 +1576,7 @@ class ra(rabase.rabase):
         self.debug('%s.request_certificate()', self.fullname)
 
         # Call CMS
-        http_status, http_reason_phrase, http_headers, http_body = \
+        http_status, http_headers, http_body = \
             self._sslget('/ca/eeca/ca/profileSubmitSSLClient',
                          self.env.ca_ee_port,
                          profileId=profile_id,
@@ -1586,7 +1586,7 @@ class ra(rabase.rabase):
         # Parse and handle errors
         if http_status != 200:
             self.raise_certificate_operation_error('request_certificate',
-                                                   detail=http_reason_phrase)
+                                                   detail=http_status)
 
         parse_result = self.get_parse_result_xml(http_body, 
parse_profile_submit_result_xml)
         # Note different status return, it's not request_status, it's 
error_code
@@ -1655,7 +1655,7 @@ class ra(rabase.rabase):
         serial_number = int(serial_number, 0)
 
         # Call CMS
-        http_status, http_reason_phrase, http_headers, http_body = \
+        http_status, http_headers, http_body = \
             self._sslget('/ca/agent/ca/doRevoke',
                          self.env.ca_agent_port,
                          op='revoke',
@@ -1667,7 +1667,7 @@ class ra(rabase.rabase):
         # Parse and handle errors
         if http_status != 200:
             self.raise_certificate_operation_error('revoke_certificate',
-                                                   detail=http_reason_phrase)
+                                                   detail=http_status)
 
         parse_result = self.get_parse_result_xml(http_body, 
parse_revoke_cert_xml)
         request_status = parse_result['request_status']
@@ -1718,7 +1718,7 @@ class ra(rabase.rabase):
         serial_number = int(serial_number, 0)
 
         # Call CMS
-        http_status, http_reason_phrase, http_headers, http_body = \
+        http_status, http_headers, http_body = \
             self._sslget('/ca/agent/ca/doUnrevoke',
                          self.env.ca_agent_port,
                          serialNumber=str(serial_number),
@@ -1727,7 +1727,7 @@ class ra(rabase.rabase):
         # Parse and handle errors
         if http_status != 200:
             self.raise_certificate_operation_error('take_certificate_off_hold',
-                                                   detail=http_reason_phrase)
+                                                   detail=http_status)
 
 
         parse_result = self.get_parse_result_xml(http_body, 
parse_unrevoke_cert_xml)
@@ -2030,7 +2030,7 @@ class RestClient(Backend):
         """Log into the REST API"""
         if self.cookie is not None:
             return
-        status, status_text, resp_headers, resp_body = dogtag.https_request(
+        status, resp_headers, resp_body = dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
             '/ca/rest/account/login',
             self.sec_dir, self.password, self.ipa_certificate_nickname,
@@ -2056,8 +2056,8 @@ class RestClient(Backend):
         """
         :param url: The URL to post to.
         :param kw:  Keyword arguments to encode into POST body.
-        :return:   (http_status, http_reason_phrase, http_headers, http_body)
-                   as (integer, unicode, dict, str)
+        :return:   (http_status, http_headers, http_body)
+                   as (integer, dict, str)
 
         Perform an HTTPS request
         """
@@ -2071,7 +2071,7 @@ class RestClient(Backend):
         resource = os.path.join('/ca/rest', self.path, path)
 
         # perform main request
-        status, status_text, resp_headers, resp_body = dogtag.https_request(
+        status, resp_headers, resp_body = dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
             resource,
             self.sec_dir, self.password, self.ipa_certificate_nickname,
@@ -2080,10 +2080,10 @@ class RestClient(Backend):
         if status < 200 or status >= 300:
             explanation = self._parse_dogtag_error(resp_body) or ''
             raise errors.RemoteRetrieveError(
-                reason=_('Non-2xx response from CA REST API: %(status)d 
%(status_text)s. %(explanation)s')
-                % {'status': status, 'status_text': status_text, 
'explanation': explanation}
+                reason=_('Non-2xx response from CA REST API: %(status)d. 
%(explanation)s')
+                % {'status': status, 'explanation': explanation}
             )
-        return (status, status_text, resp_headers, resp_body)
+        return (status, resp_headers, resp_body)
 
 
 class ra_certprofile(RestClient):
@@ -2108,7 +2108,7 @@ class ra_certprofile(RestClient):
         """
         Read the profile configuration from Dogtag
         """
-        status, status_text, resp_headers, resp_body = self._ssldo(
+        status, resp_headers, resp_body = self._ssldo(
             'GET', profile_id + '/raw')
         return resp_body
 
-- 
2.5.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to