From 5016ddbb9173cabfebb174116b3703e5c7c75a06 Mon Sep 17 00:00:00 2001
From: Petr Cech <pcech@redhat.com>
Date: Tue, 16 Aug 2016 14:59:06 +0200
Subject: [PATCH 3/3] UTIL: Add LD_PRELOAD module for debugging ldb_*
 operations

This patch adds 4 wrappers:
* ldb_add()
* ldb_delete()
* ldb_modify()
* ldb_rename()

Adding and modifying produce full ldif.
Deleting and renaming produce only short message.

Usage:
export LD_PRELOAD=/path/to/module/sss_ldb_debug.so
and restart sssd.

Resolves:
https://fedorahosted.org/sssd/ticket/3060
---
 Makefile.am              |  16 ++++
 src/tests/dlopen-tests.c |   1 +
 src/util/sss_ldb_debug.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 203 insertions(+)
 create mode 100644 src/util/sss_ldb_debug.c

diff --git a/Makefile.am b/Makefile.am
index 64328766e09213167dc392ed307c75e318845ed3..e08fcbb2d92d98b7b24c17ebca2edc999602cd08 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1001,6 +1001,22 @@ libsss_util_la_LIBADD += stap_generated_probes.lo
 endif
 libsss_util_la_LDFLAGS = -avoid-version
 
+noinst_LTLIBRARIES += sss_ldb_debug.la
+sss_ldb_debug_la_SOURCES= \
+    src/util/sss_ldb_debug.c \
+    $(NULL)
+sss_ldb_debug_la_LIBADD = \
+    $(LIBADD_DL) \
+    $(LDB_LIBS) \
+    libsss_debug.la \
+    $(NULL)
+sss_ldb_debug_la_LDFLAGS = \
+    -shared \
+    -module \
+    -avoid-version \
+    -rpath $(abs_top_builddir) \
+    $(NULL)
+
 pkglib_LTLIBRARIES += libsss_semanage.la
 libsss_semanage_la_CFLAGS = \
     $(AM_CFLAGS) \
diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c
index 96cc4db9b56d2edc9b3a48e02f4b62f1afa6848a..01a9edc59f3e68a7ca71ddc3c0e7fefc7ec81397 100644
--- a/src/tests/dlopen-tests.c
+++ b/src/tests/dlopen-tests.c
@@ -113,6 +113,7 @@ struct so {
 #ifdef HAVE_CONFIG_LIB
     { "libsss_config.so", { LIBPFX"libsss_config.so", NULL } },
 #endif
+    { "sss_ldb_debug.so", { LIBPFX"sss_ldb_debug.so", NULL } },
     { NULL }
 };
 
diff --git a/src/util/sss_ldb_debug.c b/src/util/sss_ldb_debug.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f8cf0322674c4f4c0949a0e7b2c3f9fa175032c
--- /dev/null
+++ b/src/util/sss_ldb_debug.c
@@ -0,0 +1,186 @@
+/*
+    SSSD
+
+    Authors:
+        Lukas Slebodnik <lslebodn@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+typedef int errno_t;
+
+#include "config.h"
+
+#include <string.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <ldb.h>
+#include "util/debug.h"
+
+struct sss_ldif_vprint_ctx {
+    char *ldif;
+};
+
+#define SSS_DEBUG_LEVEL SSSDBG_TRACE_ALL
+
+static int ldif_vprintf_fn(void *private_data, const char *fmt, ...)
+{
+    struct sss_ldif_vprint_ctx *print_ctx;
+    va_list ap;
+    int lenght = 0;
+
+    /* Note that the function should return the number of
+     * bytes written, or a negative error code.
+     */
+
+    print_ctx = talloc_get_type(private_data, struct sss_ldif_vprint_ctx);
+
+    if (print_ctx == NULL) {
+        return - ENOMEM;
+    }
+
+    if (fmt != NULL) {
+        va_start(ap, fmt);
+
+        if (print_ctx->ldif != NULL) {
+            lenght = strlen(print_ctx->ldif);
+        }
+
+        print_ctx->ldif = talloc_vasprintf_append_buffer(print_ctx->ldif,
+                                                         fmt, ap);
+        va_end(ap);
+
+        if (print_ctx->ldif == NULL) {
+            return - ENOENT;
+        }
+
+        lenght = strlen(print_ctx->ldif) - lenght;
+    }
+
+    return lenght;
+}
+
+static void sss_ldb_ldif2log(enum ldb_changetype changetype,
+                             struct ldb_context *ldb,
+                             const struct ldb_message *message)
+{
+    int ret;
+    struct ldb_ldif ldif;
+    struct sss_ldif_vprint_ctx *ldb_print_ctx;
+    const char *function;
+
+    switch (changetype) {
+    case LDB_CHANGETYPE_ADD:
+        function = "ldb_add";
+        break;
+    case LDB_CHANGETYPE_MODIFY:
+        function = "ldb_modify";
+        break;
+    default:
+        function = __FUNCTION__;
+        break;
+    }
+
+    ldb_print_ctx = talloc_zero(ldb, struct sss_ldif_vprint_ctx);
+    if (ldb_print_ctx == NULL) {
+        return;
+    }
+    ldb_print_ctx->ldif = NULL;
+
+    ldif.changetype = changetype;
+    ldif.msg = (void *)(intptr_t)message;
+
+    ret = ldb_ldif_write(ldb, ldif_vprintf_fn, ldb_print_ctx, &ldif);
+    if (ret < 0) {
+        ret = - ret;
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "ldb_ldif_write() failed with [%d][%s].\n",
+              ret, strerror(ret));
+        goto done;
+    }
+
+    if (DEBUG_IS_SET(SSS_DEBUG_LEVEL)) {
+        sss_debug_fn(__FILE__, __LINE__, function,
+                     SSS_DEBUG_LEVEL,
+                     "ldif\n[\n%s]\n", ldb_print_ctx->ldif);
+    }
+
+done:
+    talloc_free(ldb_print_ctx->ldif);
+    talloc_free(ldb_print_ctx);
+
+    return;
+}
+
+typedef typeof(ldb_add) ldb_add_fn;
+static ldb_add_fn *orig_ldb_add = NULL;
+
+int ldb_add(struct ldb_context *ldb, const struct ldb_message *message)
+{
+    if (orig_ldb_add == NULL) {
+        orig_ldb_add = dlsym(RTLD_NEXT, "ldb_add");
+    }
+
+    sss_ldb_ldif2log(LDB_CHANGETYPE_ADD, ldb, message);
+
+    return orig_ldb_add(ldb, message);
+}
+
+typedef typeof(ldb_delete) ldb_delete_fn;
+static ldb_delete_fn *orig_ldb_delete = NULL;
+
+int ldb_delete(struct ldb_context *ldb, struct ldb_dn *dn)
+{
+    if (orig_ldb_delete == NULL) {
+        orig_ldb_delete = dlsym(RTLD_NEXT, "ldb_delete");
+    }
+
+    DEBUG(SSS_DEBUG_LEVEL, "Deleting [%s]\n", ldb_dn_get_linearized(dn));
+
+    return orig_ldb_delete(ldb, dn);
+}
+
+typedef typeof(ldb_modify) ldb_modify_fn;
+static ldb_modify_fn *orig_ldb_modify = NULL;
+
+int ldb_modify(struct ldb_context *ldb, const struct ldb_message *message)
+{
+    if (orig_ldb_modify == NULL) {
+        orig_ldb_modify = dlsym(RTLD_NEXT, "ldb_modify");
+    }
+
+    sss_ldb_ldif2log(LDB_CHANGETYPE_MODIFY, ldb, message);
+
+    return orig_ldb_modify(ldb, message);
+}
+
+typedef typeof(ldb_rename) ldb_rename_fn;
+static ldb_rename_fn *orig_ldb_rename = NULL;
+
+int ldb_rename(struct ldb_context *ldb,
+               struct ldb_dn *olddn,
+               struct ldb_dn *newdn)
+{
+    if (orig_ldb_rename == NULL) {
+        orig_ldb_rename = dlsym(RTLD_NEXT, "ldb_rename");
+    }
+
+    DEBUG(SSS_DEBUG_LEVEL,
+          "Renaming [%s] to [%s]\n",
+          ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
+
+    return orig_ldb_rename(ldb, olddn, newdn);
+}
-- 
2.7.4

