--
David Kupka
From ece6e155007e5ab1c13c4cb61977fec5c68c8e51 Mon Sep 17 00:00:00 2001
From: David Kupka <dku...@redhat.com>
Date: Wed, 1 Jul 2015 16:26:15 +0200
Subject: [PATCH] cermonger: Use private unix socket when DBus SystemBus is not
 available.

---
 ipaplatform/base/paths.py |   1 +
 ipapython/certmonger.py   | 125 +++++++++++++++++++++++++++++++++-------------
 2 files changed, 91 insertions(+), 35 deletions(-)

diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index e94cb9d02a8d429a5c19a935766486d5e60c97ad..52ab156c907158d6f52ca4ffc876c01cd5ffa01e 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -346,6 +346,7 @@ class BasePathNamespace(object):
     BAK2DB = '/usr/sbin/bak2db'
     DB2BAK = '/usr/sbin/db2bak'
     KDCPROXY_CONFIG = '/etc/ipa/kdcproxy/kdcproxy.conf'
+    CERTMONGER = '/usr/sbin/certmonger'
 
 
 path_namespace = BasePathNamespace
diff --git a/ipapython/certmonger.py b/ipapython/certmonger.py
index 4baaaa85da08bb943d6b9f0091a1d2acc36b18d6..09e1ce78609f205c7e633053c1fab36d095ee07d 100644
--- a/ipapython/certmonger.py
+++ b/ipapython/certmonger.py
@@ -27,6 +27,8 @@ import sys
 import time
 import dbus
 import shlex
+import subprocess
+import tempfile
 from ipapython import ipautil
 from ipapython import dogtag
 from ipapython.ipa_log_manager import *
@@ -39,12 +41,11 @@ DBUS_CM_REQUEST_IF = 'org.fedorahosted.certmonger.request'
 DBUS_CM_CA_IF = 'org.fedorahosted.certmonger.ca'
 DBUS_PROPERTY_IF = 'org.freedesktop.DBus.Properties'
 
-
 class _cm_dbus_object(object):
     """
     Auxiliary class for convenient DBus object handling.
     """
-    def __init__(self, bus, object_path, object_dbus_interface,
+    def __init__(self, bus, parent, object_path, object_dbus_interface,
                  parent_dbus_interface=None, property_interface=False):
         """
         bus - DBus bus object, result of dbus.SystemBus() or dbus.SessionBus()
@@ -60,6 +61,7 @@ class _cm_dbus_object(object):
         if parent_dbus_interface is None:
             parent_dbus_interface = object_dbus_interface
         self.bus = bus
+        self.parent = parent
         self.path = object_path
         self.obj_dbus_if = object_dbus_interface
         self.parent_dbus_if = parent_dbus_interface
@@ -74,31 +76,83 @@ def _start_certmonger():
     Start certmonger daemon. If it's already running systemctl just ignores
     the command.
     """
-    if not services.knownservices.certmonger.is_running():
-        try:
-            services.knownservices.certmonger.start()
-        except Exception, e:
-            root_logger.error('Failed to start certmonger: %s' % e)
-            raise
-
-
-def _connect_to_certmonger():
-    """
-    Start certmonger daemon and connect to it via DBus.
-    """
     try:
-        _start_certmonger()
-    except (KeyboardInterrupt, OSError), e:
+        services.knownservices.certmonger.start()
+    except Exception, e:
         root_logger.error('Failed to start certmonger: %s' % e)
         raise
 
-    try:
-        bus = dbus.SystemBus()
-        cm = _cm_dbus_object(bus, DBUS_CM_PATH, DBUS_CM_IF)
-    except dbus.DBusException, e:
-        root_logger.error("Failed to access certmonger over DBus: %s", e)
-        raise
-    return cm
+
+def _private_conn_start():
+    sock_dir = tempfile.mkdtemp()
+    sock_filename = os.path.join(sock_dir, 'certmonger')
+    proc = subprocess.Popen([paths.CERTMONGER, '-n', '-L', '-P', sock_filename], stderr=subprocess.STDOUT)
+    while not os.path.exists(sock_filename):
+        time.sleep(1)
+    unix_sock = "unix:path=%s" % sock_filename
+    return (unix_sock, proc)
+
+
+def _private_conn_stop(proc, timeout=30):
+    retcode = proc.poll()
+    if retcode is not None:
+        return
+    proc.terminate()
+    for i in range(0, timeout, 5):
+        retcode = proc.poll()
+        if retcode is not None:
+            return
+        time.sleep(5)
+    proc.kill()
+
+
+class _certmonger(_cm_dbus_object):
+    """
+    Create a connection to certmonger.
+    By default use SystemBus. When not available use private connection
+    over Unix socket.
+    This solution is really ugly and should be removed as soon as DBus
+    SystemBus is available at system install time.
+    """
+    _bus = None
+    _private_sock = None
+    _proc = None
+
+    def __del__(self):
+        if self._proc:
+            _private_conn_stop(self._proc)
+
+    def __init__(self):
+        if self._bus and self._bus.get_is_connected():
+            return
+
+        try:
+            self._bus = dbus.SystemBus()
+        except dbus.DBusException as e:
+            err_name = e.get_dbus_name()
+            if err_name == 'org.freedesktop.DBus.Error.ServiceUnknown':
+                try:
+                    _start_certmonger()
+                except Exception as e:
+                    root_logger.error("Failed to start certmonger: %s" % e)
+                else:
+                    try:
+                        self._bus = dbus.SystemBus()
+                    except dbus.DBusException as e:
+                        err_name = e.get_dbus_name()
+
+            if not self._bus:
+                if err_name not in ['org.freedesktop.DBus.Error.NoServer',
+                        'org.freedesktop.DBus.Error.FileNotFound']:
+                    root_logger.error("Failed to connect to certmonger over SystemBus: %s" % e)
+                    raise
+                try:
+                    (self._private_sock, self._proc) = _private_conn_start()
+                    self._bus = dbus.connection.Connection(self._private_sock)
+                except dbus.DBusException as e:
+                    root_logger.error("Failed to connect to certmonger over private socket: %s" % e)
+                    raise
+        super(_certmonger, self).__init__(self._bus, None, DBUS_CM_PATH, DBUS_CM_IF)
 
 
 def _get_requests(criteria=dict()):
@@ -108,7 +162,7 @@ def _get_requests(criteria=dict()):
     if not isinstance(criteria, dict):
         raise TypeError('"criteria" must be dict.')
 
-    cm = _connect_to_certmonger()
+    cm = _certmonger()
     requests = []
     requests_paths = []
     if 'nickname' in criteria:
@@ -119,12 +173,12 @@ def _get_requests(criteria=dict()):
         requests_paths = cm.obj_if.get_requests()
 
     for request_path in requests_paths:
-        request = _cm_dbus_object(cm.bus, request_path, DBUS_CM_REQUEST_IF,
+        request = _cm_dbus_object(cm.bus, cm, request_path, DBUS_CM_REQUEST_IF,
                                   DBUS_CM_IF, True)
         for criterion in criteria:
             if criterion == 'ca-name':
                 ca_path = request.obj_if.get_ca()
-                ca = _cm_dbus_object(cm.bus, ca_path, DBUS_CM_CA_IF,
+                ca = _cm_dbus_object(cm.bus, cm, ca_path, DBUS_CM_CA_IF,
                                      DBUS_CM_IF)
                 value = ca.obj_if.get_nickname()
             else:
@@ -133,6 +187,7 @@ def _get_requests(criteria=dict()):
                 break
         else:
             requests.append(request)
+
     return requests
 
 
@@ -166,7 +221,7 @@ def get_request_value(request_id, directive):
     if request:
         if directive == 'ca-name':
             ca_path = request.obj_if.get_ca()
-            ca = _cm_dbus_object(request.bus, ca_path, DBUS_CM_CA_IF,
+            ca = _cm_dbus_object(request.bus, request, ca_path, DBUS_CM_CA_IF,
                                  DBUS_CM_IF)
             return ca.obj_if.get_nickname()
         else:
@@ -250,7 +305,7 @@ def request_cert(nssdb, nickname, subject, principal, passwd_fname=None):
     """
     Execute certmonger to request a server certificate.
     """
-    cm = _connect_to_certmonger()
+    cm = _certmonger()
     ca_path = cm.obj_if.find_ca_by_nickname('IPA')
     if not ca_path:
         raise RuntimeError('IPA CA not found')
@@ -264,7 +319,7 @@ def request_cert(nssdb, nickname, subject, principal, passwd_fname=None):
     result = cm.obj_if.add_request(request_parameters)
     try:
         if result[0]:
-            request = _cm_dbus_object(cm.bus, result[1], DBUS_CM_REQUEST_IF,
+            request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
                                       DBUS_CM_IF, True)
     except TypeError:
         root_logger.error('Failed to get create new request.')
@@ -283,7 +338,7 @@ def start_tracking(nickname, secdir, password_file=None, command=None):
 
     Returns certificate nickname.
     """
-    cm = _connect_to_certmonger()
+    cm = _certmonger()
     params = {'TRACK': True}
     params['cert-nickname'] = nickname
     params['cert-database'] = os.path.abspath(secdir)
@@ -302,7 +357,7 @@ def start_tracking(nickname, secdir, password_file=None, command=None):
     result = cm.obj_if.add_request(params)
     try:
         if result[0]:
-            request = _cm_dbus_object(cm.bus, result[1], DBUS_CM_REQUEST_IF,
+            request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
                                       DBUS_CM_IF, True)
     except TypeError, e:
         root_logger.error('Failed to add new request.')
@@ -330,7 +385,7 @@ def stop_tracking(secdir, request_id=None, nickname=None):
         root_logger.error('Failed to get request: %s' % e)
         raise
     if request:
-        cm = _connect_to_certmonger()
+        cm = _certmonger()
         cm.obj_if.remove_request(request.path)
 
 
@@ -357,9 +412,9 @@ def _find_IPA_ca():
     We can use find_request_value because the ca files have the
     same file format.
     """
-    cm = _connect_to_certmonger()
+    cm = _certmonger()
     ca_path = cm.obj_if.find_ca_by_nickname('IPA')
-    return _cm_dbus_object(cm.bus, ca_path, DBUS_CM_CA_IF, DBUS_CM_IF, True)
+    return _cm_dbus_object(cm.bus, cm, ca_path, DBUS_CM_CA_IF, DBUS_CM_IF, True)
 
 
 def add_principal_to_cas(principal):
@@ -423,7 +478,7 @@ def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
     Both commands can be None.
     """
 
-    cm = _connect_to_certmonger()
+    cm = _certmonger()
     certmonger_cmd_template = paths.CERTMONGER_COMMAND_TEMPLATE
 
     params = {'TRACK': True}
-- 
2.4.3

-- 
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