Modififed NSSConnection not to shutdown existing database.

The NSSConnection class has been modified not to shutdown the
existing NSS database if the database is already opened to
establish an SSL connection, or is already opened by another
code that uses an NSS database without establishing an SSL
connection such as vault CLIs.

https://fedorahosted.org/freeipa/ticket/4638

Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
Endi S. Dewata 2014-09-16 20:11:35 -04:00 committed by Petr Viktorin
parent 74e0a8cebc
commit 80a8df3f19
2 changed files with 42 additions and 27 deletions

View File

@ -63,6 +63,7 @@ from ipaplatform.paths import paths
from ipapython.cookie import Cookie from ipapython.cookie import Cookie
from ipapython.dnsutil import DNSName from ipapython.dnsutil import DNSName
from ipalib.text import _ from ipalib.text import _
import ipapython.nsslib
from ipapython.nsslib import NSSHTTPS, NSSConnection from ipapython.nsslib import NSSHTTPS, NSSConnection
from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \ from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE
@ -450,14 +451,10 @@ class LanguageAwareTransport(MultiProtocolTransport):
class SSLTransport(LanguageAwareTransport): class SSLTransport(LanguageAwareTransport):
"""Handles an HTTPS transaction to an XML-RPC server.""" """Handles an HTTPS transaction to an XML-RPC server."""
def __nss_initialized(self, dbdir): def get_connection_dbdir(self):
""" """
If there is another connections open it may have already If there is a connections open it may have already initialized
initialized NSS. This is likely to lead to an NSS shutdown NSS database. Return the database location used by the connection.
failure. One way to mitigate this is to tell NSS to not
initialize if it has already been done in another open connection.
Returns True if another connection is using the same db.
""" """
for value in context.__dict__.values(): for value in context.__dict__.values():
if not isinstance(value, Connection): if not isinstance(value, Connection):
@ -466,25 +463,32 @@ class SSLTransport(LanguageAwareTransport):
getattr(value.conn, '_ServerProxy__transport', None), getattr(value.conn, '_ServerProxy__transport', None),
SSLTransport): SSLTransport):
continue continue
if hasattr(value.conn._ServerProxy__transport, 'dbdir') and \ if hasattr(value.conn._ServerProxy__transport, 'dbdir'):
value.conn._ServerProxy__transport.dbdir == dbdir: return value.conn._ServerProxy__transport.dbdir
return True return None
return False
def make_connection(self, host): def make_connection(self, host):
host, self._extra_headers, x509 = self.get_host_info(host) host, self._extra_headers, x509 = self.get_host_info(host)
# Python 2.7 changed the internal class used in xmlrpclib from # Python 2.7 changed the internal class used in xmlrpclib from
# HTTP to HTTPConnection. We need to use the proper subclass # HTTP to HTTPConnection. We need to use the proper subclass
# If we an existing connection exists using the same NSS database
# there is no need to re-initialize. Pass thsi into the NSS
# connection creator.
if sys.version_info >= (2, 7): if sys.version_info >= (2, 7):
if self._connection and host == self._connection[0]: if self._connection and host == self._connection[0]:
return self._connection[1] return self._connection[1]
dbdir = getattr(context, 'nss_dir', paths.IPA_NSSDB_DIR) dbdir = getattr(context, 'nss_dir', paths.IPA_NSSDB_DIR)
no_init = self.__nss_initialized(dbdir) connection_dbdir = self.get_connection_dbdir()
if connection_dbdir:
# If an existing connection is already using the same NSS
# database there is no need to re-initialize.
no_init = dbdir == connection_dbdir
else:
# If the NSS database is already being used there is no
# need to re-initialize.
no_init = dbdir == ipapython.nsslib.current_dbdir
if sys.version_info < (2, 7): if sys.version_info < (2, 7):
conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init) conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init)
else: else:

View File

@ -31,6 +31,9 @@ import nss.ssl as ssl
import nss.error as error import nss.error as error
from ipaplatform.paths import paths from ipaplatform.paths import paths
# NSS database currently open
current_dbdir = None
def auth_certificate_callback(sock, check_sig, is_server, certdb): def auth_certificate_callback(sock, check_sig, is_server, certdb):
cert_is_valid = False cert_is_valid = False
@ -184,19 +187,27 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback):
httplib.HTTPConnection.__init__(self, host, port, strict) httplib.HTTPConnection.__init__(self, host, port, strict)
NSSAddressFamilyFallback.__init__(self, family) NSSAddressFamilyFallback.__init__(self, family)
if not dbdir:
raise RuntimeError("dbdir is required")
root_logger.debug('%s init %s', self.__class__.__name__, host) root_logger.debug('%s init %s', self.__class__.__name__, host)
if not no_init and nss.nss_is_initialized():
# close any open NSS database and use the new one # If initialization is requested, initialize the new database.
if not no_init:
if nss.nss_is_initialized():
ssl.clear_session_cache() ssl.clear_session_cache()
try: try:
nss.nss_shutdown() nss.nss_shutdown()
except NSPRError, e: except NSPRError, e:
if e.errno != error.SEC_ERROR_NOT_INITIALIZED: if e.errno != error.SEC_ERROR_NOT_INITIALIZED:
raise e raise e
if not dbdir:
raise RuntimeError("dbdir is required")
nss.nss_init(dbdir) nss.nss_init(dbdir)
global current_dbdir
current_dbdir = dbdir
ssl.set_domestic_policy() ssl.set_domestic_policy()
nss.set_password_callback(self.password_callback) nss.set_password_callback(self.password_callback)