590d18
From c50f7610b1781ccbd6f169805d29d84e34e46db8 Mon Sep 17 00:00:00 2001
590d18
From: Petr Spacek <pspacek@redhat.com>
590d18
Date: Mon, 31 Aug 2015 18:01:12 +0200
590d18
Subject: [PATCH] DNSSEC: Fix deadlock in ipa-ods-exporter <-> ods-enforcerd
590d18
 interaction
590d18
590d18
https://fedorahosted.org/freeipa/ticket/5273
590d18
590d18
Reviewed-By: Martin Basti <mbasti@redhat.com>
590d18
Reviewed-By: Oleg Fayans <ofayans@redhat.com>
590d18
---
590d18
 daemons/dnssec/ipa-ods-exporter | 39 +++++++++++++++++++++++++++++++--------
590d18
 1 file changed, 31 insertions(+), 8 deletions(-)
590d18
590d18
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
590d18
index 9544db149aed6574a8962d6c8e6b6f1bc520d6db..76c7e484c65888b3d722448ee669ca8d95e3f3d9 100755
590d18
--- a/daemons/dnssec/ipa-ods-exporter
590d18
+++ b/daemons/dnssec/ipa-ods-exporter
590d18
@@ -368,12 +368,12 @@ def parse_command(cmd):
590d18
     """
590d18
     if cmd == 'ipa-hsm-update':
590d18
         return (0,
590d18
-                'HSM synchronization finished, exiting.',
590d18
+                'HSM synchronization finished, skipping zone synchronization.',
590d18
                 None)
590d18
 
590d18
     elif cmd == 'ipa-full-update':
590d18
         return (None,
590d18
-                'Synchronization of all zones requested.',
590d18
+                'Synchronization of all zones was finished.',
590d18
                 None)
590d18
 
590d18
     elif not cmd.startswith('update '):
590d18
@@ -386,7 +386,7 @@ def parse_command(cmd):
590d18
     else:
590d18
         zone_name = cmd2ods_zone_name(cmd)
590d18
         return (None,
590d18
-                'Update request for zone "%s" queued.\n' % zone_name,
590d18
+                'Zone was "%s" updated.\n' % zone_name,
590d18
                 zone_name)
590d18
 
590d18
 def send_systemd_reply(conn, reply):
590d18
@@ -541,18 +541,29 @@ except KeyError as e:
590d18
 
590d18
 exitcode, msg, zone_name = parse_command(cmd)
590d18
 
590d18
-if conn:
590d18
-    send_systemd_reply(conn, msg)
590d18
 if exitcode is not None:
590d18
+    if conn:
590d18
+        send_systemd_reply(conn, msg)
590d18
     log.info(msg)
590d18
     sys.exit(exitcode)
590d18
 else:
590d18
     log.debug(msg)
590d18
 
590d18
 # Open DB directly and read key timestamps etc.
590d18
-with ods_db_lock():
590d18
-    db = sqlite3.connect(paths.OPENDNSSEC_KASP_DB,
590d18
-            isolation_level="EXCLUSIVE")
590d18
+db = None
590d18
+try:
590d18
+    # LOCK WARNING:
590d18
+    # ods-enforcerd is holding kasp.db.our_lock when processing all zones and
590d18
+    # the lock is unlocked only after all calls to ods-signer are finished,
590d18
+    # i.e. when ods-enforcerd receives reply from each ods-signer call.
590d18
+    #
590d18
+    # Consequently, ipa-ods-exporter (ods-signerd implementation) must not
590d18
+    # request kasp.db.our_lock to prevent deadlocks.
590d18
+    # SQLite transaction isolation should suffice.
590d18
+    # Beware: Reply can be sent back only after DB is unlocked and closed
590d18
+    #         otherwise ods-enforcerd will fail.
590d18
+
590d18
+    db = sqlite3.connect(paths.OPENDNSSEC_KASP_DB)
590d18
     db.row_factory = sqlite3.Row
590d18
     db.execute('BEGIN')
590d18
 
590d18
@@ -564,4 +575,16 @@ with ods_db_lock():
590d18
         for zone_row in db.execute("SELECT name FROM zones"):
590d18
             sync_zone(log, ldap, dns_dn, zone_row['name'])
590d18
 
590d18
+except Exception as ex:
590d18
+    msg = "ipa-ods-exporter exception: %s" % ex
590d18
+    raise ex
590d18
+
590d18
+finally:
590d18
+    try:
590d18
+        if db:
590d18
+            db.close()
590d18
+    finally:
590d18
+        if conn:
590d18
+            send_systemd_reply(conn, msg)
590d18
+
590d18
 log.debug('Done')
590d18
-- 
590d18
2.5.1
590d18