Imported Upstream version 4.8.10
This commit is contained in:
@@ -17,132 +17,58 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import httplib
|
||||
import collections
|
||||
import gzip
|
||||
import io
|
||||
import logging
|
||||
from urllib.parse import urlencode
|
||||
import xml.dom.minidom
|
||||
import ConfigParser
|
||||
from urllib import urlencode
|
||||
import zlib
|
||||
|
||||
import nss.nss as nss
|
||||
from nss.error import NSPRError
|
||||
import six
|
||||
|
||||
# pylint: disable=ipa-forbidden-import
|
||||
from ipalib import api, errors
|
||||
from ipalib.errors import NetworkError, CertificateOperationError
|
||||
from ipalib.util import create_https_connection
|
||||
from ipalib.errors import NetworkError
|
||||
from ipalib.text import _
|
||||
from ipapython import nsslib, ipautil
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.ipa_log_manager import *
|
||||
# pylint: enable=ipa-forbidden-import
|
||||
from ipapython import ipautil
|
||||
|
||||
# IPA can use either Dogtag version 9 or 10.
|
||||
#
|
||||
# Install tools should use the constants from install_constants, so that they
|
||||
# install with version 10 if it is available, and with 9 if not.
|
||||
# After IPA installation, the Dogtag version used is stored in the
|
||||
# "dogtag_version" config option. (If that is missing, version 9 is assumed.)
|
||||
# The configured_constants() function below provides constants relevant to
|
||||
# the configured version.
|
||||
# Python 3 rename. The package is available in "six.moves.http_client", but
|
||||
# pylint cannot handle classes from that alias
|
||||
try:
|
||||
import httplib
|
||||
except ImportError:
|
||||
# pylint: disable=import-error
|
||||
import http.client as httplib
|
||||
|
||||
class Dogtag10Constants(object):
|
||||
DOGTAG_VERSION = 10
|
||||
UNSECURE_PORT = 8080
|
||||
AGENT_SECURE_PORT = 8443
|
||||
EE_SECURE_PORT = 8443
|
||||
AJP_PORT = 8009
|
||||
DS_PORT = 389
|
||||
DS_SECURE_PORT = 636
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
SPAWN_BINARY = paths.PKISPAWN
|
||||
DESTROY_BINARY = paths.PKIDESTROY
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SERVER_ROOT = paths.VAR_LIB_PKI_DIR
|
||||
PKI_INSTANCE_NAME = 'pki-tomcat'
|
||||
PKI_ROOT = '%s/%s' % (SERVER_ROOT, PKI_INSTANCE_NAME)
|
||||
CRL_PUBLISH_PATH = paths.PKI_CA_PUBLISH_DIR
|
||||
CS_CFG_PATH = '%s/conf/ca/CS.cfg' % PKI_ROOT
|
||||
PASSWORD_CONF_PATH = '%s/conf/password.conf' % PKI_ROOT
|
||||
SERVICE_PROFILE_DIR = '%s/ca/profiles/ca' % PKI_ROOT
|
||||
ALIAS_DIR = paths.PKI_TOMCAT_ALIAS_DIR.rstrip('/')
|
||||
Profile = collections.namedtuple('Profile', ['profile_id', 'description', 'store_issued'])
|
||||
|
||||
SERVICE_NAME = 'pki_tomcatd'
|
||||
INCLUDED_PROFILES = {
|
||||
Profile(u'caIPAserviceCert', u'Standard profile for network services', True),
|
||||
Profile(u'IECUserRoles', u'User profile that includes IECUserRoles extension from request', True),
|
||||
Profile(u'KDCs_PKINIT_Certs',
|
||||
u'Profile for PKINIT support by KDCs',
|
||||
False),
|
||||
}
|
||||
|
||||
RACERT_LINE_SEP = '\n'
|
||||
|
||||
IPA_SERVICE_PROFILE = '%s/caIPAserviceCert.cfg' % SERVICE_PROFILE_DIR
|
||||
SIGN_PROFILE = '%s/caJarSigningCert.cfg' % SERVICE_PROFILE_DIR
|
||||
SHARED_DB = True
|
||||
DS_USER = "dirsrv"
|
||||
DS_NAME = "dirsrv"
|
||||
DEFAULT_PROFILE = u'caIPAserviceCert'
|
||||
KDC_PROFILE = u'KDCs_PKINIT_Certs'
|
||||
|
||||
|
||||
class Dogtag9Constants(object):
|
||||
DOGTAG_VERSION = 9
|
||||
UNSECURE_PORT = 9180
|
||||
AGENT_SECURE_PORT = 9443
|
||||
EE_SECURE_PORT = 9444
|
||||
AJP_PORT = 9447
|
||||
DS_PORT = 7389
|
||||
DS_SECURE_PORT = 7636
|
||||
|
||||
SPAWN_BINARY = paths.PKICREATE
|
||||
DESTROY_BINARY = paths.PKISILENT
|
||||
|
||||
SERVER_ROOT = paths.VAR_LIB
|
||||
PKI_INSTANCE_NAME = 'pki-ca'
|
||||
PKI_ROOT = '%s/%s' % (SERVER_ROOT, PKI_INSTANCE_NAME)
|
||||
CRL_PUBLISH_PATH = paths.PKI_CA_PUBLISH_DIR
|
||||
CS_CFG_PATH = '%s/conf/CS.cfg' % PKI_ROOT
|
||||
PASSWORD_CONF_PATH = '%s/conf/password.conf' % PKI_ROOT
|
||||
SERVICE_PROFILE_DIR = '%s/profiles/ca' % PKI_ROOT
|
||||
ALIAS_DIR = '%s/alias' % PKI_ROOT
|
||||
|
||||
SERVICE_NAME = 'pki-cad'
|
||||
|
||||
RACERT_LINE_SEP = '\r\n'
|
||||
|
||||
ADMIN_SECURE_PORT = 9445
|
||||
EE_CLIENT_AUTH_PORT = 9446
|
||||
TOMCAT_SERVER_PORT = 9701
|
||||
|
||||
IPA_SERVICE_PROFILE = '%s/caIPAserviceCert.cfg' % SERVICE_PROFILE_DIR
|
||||
SIGN_PROFILE = '%s/caJarSigningCert.cfg' % SERVICE_PROFILE_DIR
|
||||
SHARED_DB = False
|
||||
DS_USER = "pkisrv"
|
||||
DS_NAME = "PKI-IPA"
|
||||
|
||||
if os.path.exists(paths.PKISPAWN):
|
||||
install_constants = Dogtag10Constants
|
||||
if six.PY3:
|
||||
gzip_decompress = gzip.decompress # pylint: disable=no-member
|
||||
else:
|
||||
install_constants = Dogtag9Constants
|
||||
|
||||
|
||||
def _get_configured_version(api):
|
||||
"""Get the version of Dogtag IPA is configured to use
|
||||
|
||||
If an API is given, use information in its environment.
|
||||
Otherwise, use information from the global config file.
|
||||
"""
|
||||
if api:
|
||||
return int(api.env.dogtag_version)
|
||||
else:
|
||||
p = ConfigParser.SafeConfigParser()
|
||||
p.read(paths.IPA_DEFAULT_CONF)
|
||||
try:
|
||||
version = p.get('global', 'dogtag_version')
|
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
|
||||
return 9
|
||||
else:
|
||||
return int(version)
|
||||
|
||||
|
||||
def configured_constants(api=None):
|
||||
"""Get the name of the Dogtag CA instance
|
||||
|
||||
See get_configured_version
|
||||
"""
|
||||
if _get_configured_version(api) >= 10:
|
||||
return Dogtag10Constants
|
||||
else:
|
||||
return Dogtag9Constants
|
||||
# note: gzip.decompress available in Python >= 3.2
|
||||
def gzip_decompress(data):
|
||||
with gzip.GzipFile(fileobj=io.BytesIO(data)) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def error_from_xml(doc, message_template):
|
||||
@@ -150,21 +76,20 @@ def error_from_xml(doc, message_template):
|
||||
item_node = doc.getElementsByTagName("Error")
|
||||
reason = item_node[0].childNodes[0].data
|
||||
return errors.RemoteRetrieveError(reason=reason)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
return errors.RemoteRetrieveError(reason=message_template % e)
|
||||
|
||||
|
||||
def get_ca_certchain(ca_host=None, dogtag_constants=None):
|
||||
def get_ca_certchain(ca_host=None):
|
||||
"""
|
||||
Retrieve the CA Certificate chain from the configured Dogtag server.
|
||||
"""
|
||||
if ca_host is None:
|
||||
ca_host = api.env.ca_host
|
||||
if dogtag_constants is None:
|
||||
dogtag_constants = configured_constants()
|
||||
chain = None
|
||||
conn = httplib.HTTPConnection(ca_host,
|
||||
api.env.ca_install_port or dogtag_constants.UNSECURE_PORT)
|
||||
conn = httplib.HTTPConnection(
|
||||
ca_host,
|
||||
api.env.ca_install_port or 8080)
|
||||
conn.request("GET", "/ca/ee/ca/getCertChain")
|
||||
res = conn.getresponse()
|
||||
doc = None
|
||||
@@ -189,29 +114,7 @@ def get_ca_certchain(ca_host=None, dogtag_constants=None):
|
||||
return chain
|
||||
|
||||
|
||||
def ca_status(ca_host=None, use_proxy=True):
|
||||
"""Return the status of the CA, and the httpd proxy in front of it
|
||||
|
||||
The returned status can be:
|
||||
- running
|
||||
- starting
|
||||
- Service Temporarily Unavailable
|
||||
"""
|
||||
if ca_host is None:
|
||||
ca_host = api.env.ca_host
|
||||
if use_proxy:
|
||||
# Use port 443 to test the proxy as well
|
||||
ca_port = 443
|
||||
else:
|
||||
ca_port = 8443
|
||||
status, reason, headers, body = unauthenticated_https_request(
|
||||
ca_host, ca_port, '/ca/admin/ca/getStatus')
|
||||
if status == 503:
|
||||
# Service temporarily unavailable
|
||||
return reason
|
||||
elif status != 200:
|
||||
raise errors.RemoteRetrieveError(
|
||||
reason=_("Retrieving CA status failed: %s") % reason)
|
||||
def _parse_ca_status(body):
|
||||
doc = xml.dom.minidom.parseString(body)
|
||||
try:
|
||||
item_node = doc.getElementsByTagName("XMLResponse")[0]
|
||||
@@ -221,91 +124,128 @@ def ca_status(ca_host=None, use_proxy=True):
|
||||
raise error_from_xml(doc, _("Retrieving CA status failed: %s"))
|
||||
|
||||
|
||||
def https_request(host, port, url, secdir, password, nickname, **kw):
|
||||
def ca_status(ca_host=None):
|
||||
"""Return the status of the CA, and the httpd proxy in front of it
|
||||
|
||||
The returned status can be:
|
||||
- running
|
||||
- starting
|
||||
- Service Temporarily Unavailable
|
||||
"""
|
||||
if ca_host is None:
|
||||
ca_host = api.env.ca_host
|
||||
status, _headers, body = http_request(
|
||||
ca_host, 8080, '/ca/admin/ca/getStatus',
|
||||
# timeout: CA sometimes forgot to answer, we have to try again
|
||||
timeout=api.env.http_timeout)
|
||||
if status == 503:
|
||||
# Service temporarily unavailable
|
||||
return status
|
||||
elif status != 200:
|
||||
raise errors.RemoteRetrieveError(
|
||||
reason=_("Retrieving CA status failed with status %d") % status)
|
||||
return _parse_ca_status(body)
|
||||
|
||||
|
||||
def https_request(
|
||||
host, port, url, cafile, client_certfile, client_keyfile,
|
||||
method='POST', headers=None, body=None, **kw):
|
||||
"""
|
||||
:param method: HTTP request method (defalut: 'POST')
|
||||
: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
|
||||
"""
|
||||
|
||||
def connection_factory(host, port):
|
||||
conn = nsslib.NSSConnection(host, port, dbdir=secdir)
|
||||
conn.set_debuglevel(0)
|
||||
conn.connect()
|
||||
conn.sock.set_client_auth_data_callback(
|
||||
nsslib.client_auth_data_callback,
|
||||
nickname, password, nss.get_default_certdb())
|
||||
return conn
|
||||
return create_https_connection(
|
||||
host, port,
|
||||
cafile=cafile,
|
||||
client_certfile=client_certfile,
|
||||
client_keyfile=client_keyfile,
|
||||
tls_version_min=api.env.tls_version_min,
|
||||
tls_version_max=api.env.tls_version_max)
|
||||
|
||||
body = urlencode(kw)
|
||||
if body is None:
|
||||
body = urlencode(kw)
|
||||
return _httplib_request(
|
||||
'https', host, port, url, connection_factory, body)
|
||||
'https', host, port, url, connection_factory, body,
|
||||
method=method, headers=headers)
|
||||
|
||||
|
||||
def http_request(host, port, url, **kw):
|
||||
def http_request(host, port, url, timeout=None, **kw):
|
||||
"""
|
||||
:param url: The path (not complete URL!) to post to.
|
||||
:param timeout: Timeout in seconds for waiting for reply.
|
||||
: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.
|
||||
"""
|
||||
body = urlencode(kw)
|
||||
if timeout is None:
|
||||
conn_opt = {}
|
||||
else:
|
||||
conn_opt = {"timeout": timeout}
|
||||
|
||||
return _httplib_request(
|
||||
'http', host, port, url, httplib.HTTPConnection, body)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
Perform an unauthenticated HTTPS request.
|
||||
"""
|
||||
body = urlencode(kw)
|
||||
return _httplib_request(
|
||||
'https', host, port, url, httplib.HTTPSConnection, body)
|
||||
'http', host, port, url, httplib.HTTPConnection, body,
|
||||
connection_options=conn_opt)
|
||||
|
||||
|
||||
def _httplib_request(
|
||||
protocol, host, port, path, connection_factory, request_body):
|
||||
protocol, host, port, path, connection_factory, request_body,
|
||||
method='POST', headers=None, connection_options=None):
|
||||
"""
|
||||
:param request_body: Request body
|
||||
:param connection_factory: Connection class to use. Will be called
|
||||
with the host and port arguments.
|
||||
:param method: HTTP request method (default: 'POST')
|
||||
:param connection_options: a dictionary that will be passed to
|
||||
connection_factory as keyword arguments.
|
||||
|
||||
Perform a HTTP(s) request.
|
||||
"""
|
||||
if isinstance(host, unicode):
|
||||
host = host.encode('utf-8')
|
||||
uri = '%s://%s%s' % (protocol, ipautil.format_netloc(host, port), path)
|
||||
root_logger.debug('request %r', uri)
|
||||
root_logger.debug('request body %r', request_body)
|
||||
if connection_options is None:
|
||||
connection_options = {}
|
||||
|
||||
uri = u'%s://%s%s' % (protocol, ipautil.format_netloc(host, port), path)
|
||||
logger.debug('request %s %s', method, uri)
|
||||
logger.debug('request body %r', request_body)
|
||||
|
||||
headers = headers or {}
|
||||
if (
|
||||
method == 'POST'
|
||||
and 'content-type' not in (str(k).lower() for k in headers)
|
||||
):
|
||||
headers['content-type'] = 'application/x-www-form-urlencoded'
|
||||
|
||||
try:
|
||||
conn = connection_factory(host, port)
|
||||
conn.request('POST', uri,
|
||||
body=request_body,
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
)
|
||||
conn = connection_factory(host, port, **connection_options)
|
||||
conn.request(method, path, body=request_body, headers=headers)
|
||||
res = conn.getresponse()
|
||||
|
||||
http_status = res.status
|
||||
http_reason_phrase = unicode(res.reason, 'utf-8')
|
||||
http_headers = res.msg.dict
|
||||
http_headers = res.msg
|
||||
http_body = res.read()
|
||||
conn.close()
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
logger.debug("httplib request failed:", exc_info=True)
|
||||
raise NetworkError(uri=uri, error=str(e))
|
||||
|
||||
root_logger.debug('request status %d', http_status)
|
||||
root_logger.debug('request reason_phrase %r', http_reason_phrase)
|
||||
root_logger.debug('request headers %s', http_headers)
|
||||
root_logger.debug('request body %r', http_body)
|
||||
encoding = res.getheader('Content-Encoding')
|
||||
if encoding == 'gzip':
|
||||
http_body = gzip_decompress(http_body)
|
||||
elif encoding == 'deflate':
|
||||
http_body = zlib.decompress(http_body)
|
||||
|
||||
return http_status, http_reason_phrase, http_headers, http_body
|
||||
logger.debug('response status %d', http_status)
|
||||
logger.debug('response headers %s', http_headers)
|
||||
logger.debug('response body (decoded): %r', http_body)
|
||||
|
||||
return http_status, http_headers, http_body
|
||||
|
||||
Reference in New Issue
Block a user