URL: https://github.com/SSSD/sssd/pull/136
Author: spbnick
 Title: #136: Tlog integration WIP
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/136/head:pr136
git checkout pr136
From 462f3f323f3ef9aa2efcb08a6a6ba8a3c58b9a3b Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Wed, 22 Mar 2017 14:32:35 +0200
Subject: [PATCH 01/11] NSS: Move output name formatting to utils

Move NSS nss_get_name_from_msg and the core of sized_output_name to the
utils to make them available to provider and other responders.
---
 src/responder/nss/nss_protocol_grent.c |  3 +-
 src/responder/nss/nss_protocol_pwent.c |  2 +-
 src/responder/nss/nss_utils.c          | 55 +++++----------------------
 src/util/sss_nss.c                     | 68 ++++++++++++++++++++++++++++++++++
 src/util/sss_nss.h                     | 10 +++++
 5 files changed, 90 insertions(+), 48 deletions(-)

diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
index 283ab9f..5f208e0 100644
--- a/src/responder/nss/nss_protocol_grent.c
+++ b/src/responder/nss/nss_protocol_grent.c
@@ -19,6 +19,7 @@
 */
 
 #include "responder/nss/nss_protocol.h"
+#include "util/sss_nss.h"
 
 static errno_t
 nss_get_grent(TALLOC_CTX *mem_ctx,
@@ -41,7 +42,7 @@ nss_get_grent(TALLOC_CTX *mem_ctx,
     }
 
     /* Get fields. */
-    name = nss_get_name_from_msg(domain, msg);
+    name = sss_nss_get_name_from_msg(domain, msg);
     gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
 
     if (name == NULL || gid == 0) {
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
index edda9d3..c0b8e79 100644
--- a/src/responder/nss/nss_protocol_pwent.c
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -225,7 +225,7 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
 
     /* Get fields. */
     upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
-    name = nss_get_name_from_msg(domain, msg);
+    name = sss_nss_get_name_from_msg(domain, msg);
     gid = nss_get_gid(domain, msg);
     uid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_UIDNUM, 0);
 
diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c
index f839930..42fe33e 100644
--- a/src/responder/nss/nss_utils.c
+++ b/src/responder/nss/nss_utils.c
@@ -22,37 +22,11 @@
 #include <ldb.h>
 
 #include "util/util.h"
+#include "util/sss_nss.h"
 #include "confdb/confdb.h"
 #include "responder/common/responder.h"
 #include "responder/nss/nss_private.h"
 
-const char *
-nss_get_name_from_msg(struct sss_domain_info *domain,
-                      struct ldb_message *msg)
-{
-    const char *name;
-
-    /* If domain has a view associated we return overridden name
-     * if possible. */
-    if (DOM_HAS_VIEWS(domain)) {
-        name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
-                                           NULL);
-        if (name != NULL) {
-            return name;
-        }
-    }
-
-    /* Otherwise we try to return name override from
-     * Default Truest View for trusted users. */
-    name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
-    if (name != NULL) {
-        return name;
-    }
-
-    /* If no override is found we return the original name. */
-    return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-}
-
 int sized_output_name(TALLOC_CTX *mem_ctx,
                       struct resp_ctx *rctx,
                       const char *orig_name,
@@ -61,7 +35,7 @@ int sized_output_name(TALLOC_CTX *mem_ctx,
 {
     TALLOC_CTX *tmp_ctx = NULL;
     errno_t ret;
-    char *username;
+    char *name_str;
     struct sized_string *name;
 
     tmp_ctx = talloc_new(NULL);
@@ -69,30 +43,19 @@ int sized_output_name(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve,
-                               rctx->override_space);
-    if (username == NULL) {
-        ret = EIO;
-        goto done;
-    }
-
-    if (name_dom->fqnames) {
-        username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username);
-        if (username == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n");
-            ret = EIO;
-            goto done;
-        }
-    }
-
     name = talloc_zero(tmp_ctx, struct sized_string);
     if (name == NULL) {
         ret = ENOMEM;
         goto done;
     }
 
-    to_sized_string(name, username);
-    name->str = talloc_steal(name, username);
+    ret = sss_nss_output_name(mem_ctx, name_dom, orig_name,
+                              rctx->override_space, &name_str);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    to_sized_string(name, name_str);
     *_name = talloc_steal(mem_ctx, name);
     ret = EOK;
 done:
diff --git a/src/util/sss_nss.c b/src/util/sss_nss.c
index cf91a2c..54ff40a 100644
--- a/src/util/sss_nss.c
+++ b/src/util/sss_nss.c
@@ -21,6 +21,7 @@
 
 #include "util/util.h"
 #include "util/sss_nss.h"
+#include "db/sysdb.h"
 
 char *expand_homedir_template(TALLOC_CTX *mem_ctx,
                               const char *template,
@@ -219,3 +220,70 @@ char *expand_homedir_template(TALLOC_CTX *mem_ctx,
     talloc_zfree(tmp_ctx);
     return res;
 }
+
+const char *
+sss_nss_get_name_from_msg(struct sss_domain_info *domain,
+                          struct ldb_message *msg)
+{
+    const char *name;
+
+    /* If domain has a view associated we return overridden name
+     * if possible. */
+    if (DOM_HAS_VIEWS(domain)) {
+        name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
+                                           NULL);
+        if (name != NULL) {
+            return name;
+        }
+    }
+
+    /* Otherwise we try to return name override from
+     * Default Truest View for trusted users. */
+    name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
+    if (name != NULL) {
+        return name;
+    }
+
+    /* If no override is found we return the original name. */
+    return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+}
+
+int sss_nss_output_name(TALLOC_CTX *mem_ctx,
+                        struct sss_domain_info *domain,
+                        const char *name,
+                        char override_space,
+                        char **_output_name)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    errno_t ret;
+    char *output_name;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    output_name = sss_output_name(tmp_ctx, name, domain->case_preserve,
+                                  override_space);
+    if (output_name == NULL) {
+        ret = EIO;
+        goto done;
+    }
+
+    if (domain->fqnames) {
+        output_name = sss_tc_fqname(tmp_ctx, domain->names,
+                                    domain, output_name);
+        if (output_name == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n");
+            ret = EIO;
+            goto done;
+        }
+    }
+
+    *_output_name = talloc_steal(mem_ctx, output_name);
+    ret = EOK;
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
diff --git a/src/util/sss_nss.h b/src/util/sss_nss.h
index 2b8a5ae..0cf0a56 100644
--- a/src/util/sss_nss.h
+++ b/src/util/sss_nss.h
@@ -39,4 +39,14 @@ struct sss_nss_homedir_ctx {
 char *expand_homedir_template(TALLOC_CTX *mem_ctx, const char *template,
                               bool case_sensitive,
                               struct sss_nss_homedir_ctx *homedir_ctx);
+
+const char *
+sss_nss_get_name_from_msg(struct sss_domain_info *domain,
+                          struct ldb_message *msg);
+
+int sss_nss_output_name(TALLOC_CTX *mem_ctx,
+                        struct sss_domain_info *domain,
+                        const char *name,
+                        char override_space,
+                        char **_output_name);
 #endif

From f18994576910dce0ec4d885c756025941c1e5fc7 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Tue, 20 Dec 2016 10:16:47 +0200
Subject: [PATCH 02/11] CONFIG: Add session_recording section

Add information on "session_recording" config section, having three
options: "scope", "users", and "groups".

The section is intended for disabling session recording ("scope = none",
default), enabling session recording for all users ("scope = all"), and
enabling it for some specific users and/or groups ("scope = some",
"users = <users>", "groups = <groups>").
---
 src/confdb/confdb.h          | 6 ++++++
 src/config/SSSDConfigTest.py | 6 ++++--
 src/config/etc/sssd.api.conf | 6 ++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index c05b1ce..e19c0ee 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -160,6 +160,12 @@
 #define CONFDB_IFP_USER_ATTR_LIST "user_attributes"
 #define CONFDB_IFP_WILDCARD_LIMIT "wildcard_limit"
 
+/* Session Recording */
+#define CONFDB_SESSION_RECORDING_CONF_ENTRY "config/session_recording"
+#define CONFDB_SESSION_RECORDING_SCOPE "scope"
+#define CONFDB_SESSION_RECORDING_USERS "users"
+#define CONFDB_SESSION_RECORDING_GROUPS "groups"
+
 /* Domains */
 #define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s"
 #define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config"
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 457a6f0..a83c42e 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -1356,7 +1356,8 @@ def testNewConfig(self):
             'ssh',
             'pac',
             'ifp',
-            'secrets']
+            'secrets',
+            'session_recording']
         for section in control_list:
             self.assertTrue(sssdconfig.has_section(section),
                             "Section [%s] missing" %
@@ -1450,7 +1451,8 @@ def testListServices(self):
             'ssh',
             'pac',
             'ifp',
-            'secrets']
+            'secrets',
+            'session_recording']
         service_list = sssdconfig.list_services()
         for service in control_list:
             self.assertTrue(service in service_list,
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 08cecf0..0a02e4b 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -113,6 +113,12 @@ forward_headers = list, None, false
 username = str, None, false
 password = str, None, false
 
+[session_recording]
+# Session recording service
+scope = str, None, false
+users = list, str, false
+groups = list, str, false
+
 [provider]
 #Available provider types
 id_provider = str, None, true

From fc588f6e2a2a23870eb2be05f7def84b1d8bdd15 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 11 Aug 2016 14:15:55 +0300
Subject: [PATCH 03/11] BUILD: Support configuring session recording shell

Add support for specifying the shell used for recording user sessions,
at configure time.
---
 configure.ac       |  1 +
 src/conf_macros.m4 | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/configure.ac b/configure.ac
index e6a3b4e..d627fe2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -139,6 +139,7 @@ WITH_SEMANAGE
 WITH_AD_GPO_DEFAULT
 WITH_GPO_CACHE_PATH
 WITH_NOLOGIN_SHELL
+WITH_SESSION_RECORDING_SHELL
 WITH_APP_LIBS
 WITH_SUDO
 WITH_SUDO_LIB_PATH
diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
index 749e769..8d02451 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -592,6 +592,22 @@ AC_DEFUN([WITH_NOLOGIN_SHELL],
     AC_DEFINE_UNQUOTED(NOLOGIN_SHELL, "$nologin_shell", [The shell used to deny access to users])
   ])
 
+AC_DEFUN([WITH_SESSION_RECORDING_SHELL],
+  [ AC_ARG_WITH([session-recording-shell],
+                [AC_HELP_STRING([--with-session-recording-shell=PATH],
+                                [The shell used to record user sessions [/usr/bin/tlog-rec]]
+                               )
+                ]
+               )
+    session_recording_shell="/usr/bin/tlog-rec"
+    if test x"$with_session_recording_shell" != x; then
+        nologin_shell=$with_session_recording_shell
+    fi
+    AC_SUBST(session_recording_shell)
+    AC_DEFINE_UNQUOTED(SESSION_RECORDING_SHELL, "$session_recording_shell",
+                       [The shell used to record user sessions])
+  ])
+
 AC_ARG_ENABLE([all-experimental-features],
               [AS_HELP_STRING([--enable-all-experimental-features],
                               [build all experimental features])],

From d72cef16c33a7790c1bd60a1f3d01300997ce3bd Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 17 Mar 2017 12:41:02 +0200
Subject: [PATCH 04/11] UTIL: Add session recording conf management module

Add an util module for loading session recording configuration.
To be used by responders and data provider.
---
 Makefile.am                       |  1 +
 src/util/session_recording_conf.c | 72 +++++++++++++++++++++++++++++++++++++
 src/util/session_recording_conf.h | 76 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 149 insertions(+)
 create mode 100644 src/util/session_recording_conf.c
 create mode 100644 src/util/session_recording_conf.h

diff --git a/Makefile.am b/Makefile.am
index 45b04de..2f6b1f9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -632,6 +632,7 @@ dist_noinst_HEADERS = \
     src/util/io.h \
     src/util/util_errors.h \
     src/util/safe-format-string.h \
+    src/util/session_recording_conf.h \
     src/util/strtonum.h \
     src/util/sss_cli_cmd.h \
     src/util/sss_ptr_hash.h \
diff --git a/src/util/session_recording_conf.c b/src/util/session_recording_conf.c
new file mode 100644
index 0000000..f4db01b
--- /dev/null
+++ b/src/util/session_recording_conf.c
@@ -0,0 +1,72 @@
+/*
+    SSSD
+
+    Session recording configuration management
+
+    Authors:
+        Nikolai Kondrashov <nikolai.kondras...@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/>.
+*/
+
+#include "util/session_recording_conf.h"
+#include "util/debug.h"
+#include <string.h>
+#include <errno.h>
+
+errno_t session_recording_conf_load(TALLOC_CTX *mem_ctx,
+                                    struct confdb_ctx *cdb,
+                                    struct session_recording_conf *pconf)
+{
+    int ret;
+    char *str;
+
+    /* Read session_recording/scope option */
+    ret = confdb_get_string(cdb, mem_ctx, CONFDB_SESSION_RECORDING_CONF_ENTRY,
+                            CONFDB_SESSION_RECORDING_SCOPE, "none", &str);
+    if (ret != EOK) goto done;
+    if (strcasecmp(str, "none") == 0) {
+        pconf->scope = SESSION_RECORDING_CONF_SCOPE_NONE;
+    } else if (strcasecmp(str, "some") == 0) {
+        pconf->scope = SESSION_RECORDING_CONF_SCOPE_SOME;
+    } else if (strcasecmp(str, "all") == 0) {
+        pconf->scope = SESSION_RECORDING_CONF_SCOPE_ALL;
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unknown value for session recording scope: %s\n",
+              str);
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* Read session_recording/users option */
+    ret = confdb_get_string_as_list(cdb, mem_ctx,
+                                    CONFDB_SESSION_RECORDING_CONF_ENTRY,
+                                    CONFDB_SESSION_RECORDING_USERS,
+                                    &pconf->users);
+    if (ret != EOK && ret != ENOENT) goto done;
+
+    /* Read session_recording/groups option */
+    ret = confdb_get_string_as_list(cdb, mem_ctx,
+                                    CONFDB_SESSION_RECORDING_CONF_ENTRY,
+                                    CONFDB_SESSION_RECORDING_GROUPS,
+                                    &pconf->groups);
+    if (ret != EOK && ret != ENOENT) goto done;
+
+    ret = EOK;
+done:
+    return ret;
+}
diff --git a/src/util/session_recording_conf.h b/src/util/session_recording_conf.h
new file mode 100644
index 0000000..a5911e5
--- /dev/null
+++ b/src/util/session_recording_conf.h
@@ -0,0 +1,76 @@
+/*
+    SSSD
+
+    Session recording configuration management
+
+    Authors:
+        Nikolai Kondrashov <nikolai.kondras...@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/>.
+*/
+
+#ifndef __SESSION_RECORDING_CONF_H__
+#define __SESSION_RECORDING_CONF_H__
+
+#include "confdb/confdb.h"
+#include "util/util_errors.h"
+
+/** Scope of users whose session should be recorded */
+enum session_recording_conf_scope {
+    SESSION_RECORDING_CONF_SCOPE_NONE,  /**< None, no users */
+    SESSION_RECORDING_CONF_SCOPE_SOME,  /**< Some users specified elsewhere */
+    SESSION_RECORDING_CONF_SCOPE_ALL    /**< All users */
+};
+
+/** Session recording configuration (from "session_recording" section) */
+struct session_recording_conf {
+    /**
+     * Session recording scope:
+     * whether to record nobody, everyone, or some users/groups
+     */
+    enum session_recording_conf_scope   scope;
+    /**
+     * NULL-terminated list of users whose session should be recorded.
+     * Can be NULL, meaning empty list. Only applicable if scope is "some".
+     */
+    char                              **users;
+    /**
+     * NULL-terminated list of groups, members of which should have their
+     * sessions recorded. Can be NULL, meaning empty list. Only applicable if
+     * scope is "some"
+     */
+    char                              **groups;
+};
+
+/**
+ * Load session recording configuration from configuration database.
+ *
+ * @param mem_ctx   Memory context to allocate data with.
+ * @param cdb       The configuration database connection object to retrieve
+ *                  data from.
+ * @param pconf     Location for the loaded session recording configuration.
+ *
+ * @return Status code:
+ *          ENOMEM - memory allocation failed,
+ *          EINVAL - configuration was invalid,
+ *          EIO - an I/O error occurred while communicating with the ConfDB.
+ */
+extern errno_t session_recording_conf_load(
+                                    TALLOC_CTX *mem_ctx,
+                                    struct confdb_ctx *cdb,
+                                    struct session_recording_conf *pconf);
+
+#endif /* __SESSION_RECORDING_CONF_H__ */

From 54c2b47f1b404038c97fe7476d8ca37354e35cfa Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 19:10:25 +0200
Subject: [PATCH 05/11] RESPONDER: Add session recording conf loading

Add session recording configuration loading to the common responder
initialization. To be used for substituting the user shell when
session recording is enabled.
---
 Makefile.am                             | 3 +++
 src/responder/common/responder.h        | 3 +++
 src/responder/common/responder_common.c | 9 +++++++++
 src/tests/cwrap/Makefile.am             | 2 ++
 4 files changed, 17 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index 2f6b1f9..f083a66 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -553,6 +553,7 @@ SSSD_RESPONDER_OBJ = \
     src/responder/common/data_provider/rdp_client.c \
     src/monitor/monitor_iface_generated.c \
     src/providers/data_provider_req.c \
+    src/util/session_recording_conf.c \
     $(SSSD_RESPONDER_IFACE_OBJ) \
     $(SSSD_CACHE_REQ_OBJ) \
     $(NULL)
@@ -2093,6 +2094,7 @@ responder_socket_access_tests_SOURCES = \
     src/responder/common/responder_cmd.c \
     src/responder/common/data_provider/rdp_message.c \
     src/responder/common/data_provider/rdp_client.c \
+    src/util/session_recording_conf.c \
     $(SSSD_RESPONDER_IFACE_OBJ) \
     $(NULL)
 responder_socket_access_tests_CFLAGS = \
@@ -2181,6 +2183,7 @@ TEST_MOCK_RESP_OBJ = \
      src/responder/common/data_provider/rdp_message.c \
      src/responder/common/data_provider/rdp_client.c \
      src/responder/common/responder_utils.c \
+     src/util/session_recording_conf.c \
      $(SSSD_CACHE_REQ_OBJ) \
      $(SSSD_RESPONDER_IFACE_OBJ) \
      $(NULL)
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 4d1048a..53240f8 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -37,6 +37,7 @@
 #include "sbus/sssd_dbus.h"
 #include "responder/common/negcache.h"
 #include "sss_client/sss_cli.h"
+#include "util/session_recording_conf.h"
 
 extern hash_table_t *dp_requests;
 
@@ -131,6 +132,8 @@ struct resp_ctx {
     char *default_domain;
     char override_space;
 
+    struct session_recording_conf sr_conf;
+
     uint32_t cache_req_num;
 
     void *pvt_ctx;
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 76f4360..b5a7fa1 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -1163,6 +1163,15 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
         rctx->override_space = tmp[0];
     }
 
+    /* Read session_recording section */
+    ret = session_recording_conf_load(rctx, rctx->cdb, &rctx->sr_conf);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed loading session recording configuration: %s\n",
+              strerror(ret));
+        goto fail;
+    }
+
     ret = sss_monitor_init(rctx, rctx->ev, monitor_intf,
                            svc_name, svc_version, MT_SVC_SERVICE,
                            rctx, &rctx->last_request_time,
diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am
index 4a4090d..eb151ef 100644
--- a/src/tests/cwrap/Makefile.am
+++ b/src/tests/cwrap/Makefile.am
@@ -85,6 +85,7 @@ SSSD_RESPONDER_OBJ = \
     ../../../src/responder/common/data_provider/rdp_client.c \
     ../../../src/monitor/monitor_iface_generated.c \
     ../../../src/providers/data_provider_req.c \
+    ../../../src/util/session_recording_conf.c \
     $(SSSD_RESPONDER_IFACE_OBJ) \
     $(SSSD_CACHE_REQ_OBJ) \
     $(NULL)
@@ -179,6 +180,7 @@ responder_common_tests_SOURCES =\
     ../../../src/responder/common/responder_packet.c \
     ../../../src/responder/common/responder_cmd.c \
     ../../../src/tests/cmocka/common_mock_resp_dp.c \
+    ../../../src/util/session_recording_conf.c \
     $(SSSD_CACHE_REQ_OBJ) \
     $(NULL)
 responder_common_tests_CFLAGS = \

From 2c03cbe6aca03cb28b649d121d7b19c0fe0d18a3 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 17 Mar 2017 12:33:53 +0200
Subject: [PATCH 06/11] DP: Add session recording conf loading

Add session recording configuration loading to the data provider
initialization. To be used for matching users and groups with session
recording enabled.
---
 Makefile.am                      | 1 +
 src/providers/backend.h          | 2 ++
 src/providers/data_provider_be.c | 9 +++++++++
 3 files changed, 12 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index f083a66..5d84a30 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1499,6 +1499,7 @@ sssd_be_SOURCES = \
     src/providers/data_provider/dp_target_subdomains.c \
     src/providers/data_provider/dp_target_id.c \
     src/providers/data_provider/dp_target_auth.c \
+    src/util/session_recording_conf.c \
     $(SSSD_FAILOVER_OBJ)
 sssd_be_LDADD = \
     $(LIBADD_DL) \
diff --git a/src/providers/backend.h b/src/providers/backend.h
index 3054136..e6d39ef 100644
--- a/src/providers/backend.h
+++ b/src/providers/backend.h
@@ -27,6 +27,7 @@
 #include "providers/be_refresh.h"
 #include "providers/data_provider/dp.h"
 #include "util/child_common.h"
+#include "util/session_recording_conf.h"
 #include "db/sysdb.h"
 
 /* a special token, if used in place of the hostname, denotes that real
@@ -83,6 +84,7 @@ struct be_ctx {
     const char *conf_path;
     uid_t uid;
     gid_t gid;
+    struct session_recording_conf sr_conf;
     struct be_failover_ctx *be_fo;
     struct be_resolv_ctx *be_res;
 
diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 7e7b74c..f126ab0 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -427,6 +427,15 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    /* Read session_recording section */
+    ret = session_recording_conf_load(be_ctx, cdb, &be_ctx->sr_conf);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed loading session recording configuration: %s\n",
+              strerror(ret));
+        goto done;
+    }
+
     /* Initialize be_refresh periodic task. */
     be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx);
     if (be_ctx->refresh_ctx == NULL) {

From a1b06cfef8f50f08f51df09cfdef78450a6581e6 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 17 Mar 2017 12:35:51 +0200
Subject: [PATCH 07/11] SYSDB: Add sessionRecording attribute macro

Add a macro for sessionRecording attribute to sysdb.h.
To be used for storing a boolean attribute signifying if session
recording is enabled for the user.
---
 src/db/sysdb.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 83d0d79..7f70f44 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -174,6 +174,7 @@
 #define SYSDB_OVERRIDE_GROUP_CLASS "groupOverride"
 #define SYSDB_OVERRIDE_DN "overrideDN"
 #define SYSDB_OVERRIDE_OBJECT_DN "overrideObjectDN"
+#define SYSDB_SESSION_RECORDING "sessionRecording"
 
 #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
 
@@ -226,6 +227,7 @@
                         SYSDB_OVERRIDE_DN, \
                         SYSDB_OVERRIDE_OBJECT_DN, \
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
+                        SYSDB_SESSION_RECORDING, \
                         SYSDB_UUID, \
                         SYSDB_ORIG_DN, \
                         NULL}

From ebbeec59be9ca3418893616e577fbac94f9e95cd Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 17 Mar 2017 12:34:58 +0200
Subject: [PATCH 08/11] DP: Overlay sessionRecording attribute on initgr

Add sessionRecording attribute to user entries on initgr request in data
provider, specifying if the user name or groups match the ones with
session recording enabled.
---
 src/providers/data_provider/dp_target_id.c | 193 ++++++++++++++++++++++++++---
 1 file changed, 178 insertions(+), 15 deletions(-)

diff --git a/src/providers/data_provider/dp_target_id.c b/src/providers/data_provider/dp_target_id.c
index 0bca9ba..cba4cba 100644
--- a/src/providers/data_provider/dp_target_id.c
+++ b/src/providers/data_provider/dp_target_id.c
@@ -26,6 +26,7 @@
 #include "providers/data_provider/dp_iface.h"
 #include "providers/backend.h"
 #include "responder/nss/nss_iface.h"
+#include "util/sss_nss.h"
 #include "util/util.h"
 
 #define FILTER_TYPE(str, type) {str "=", sizeof(str "=") - 1, type}
@@ -73,18 +74,27 @@ static bool check_and_parse_filter(struct dp_id_data *data,
 }
 
 struct dp_initgr_ctx {
+    struct ldb_dn *dn;
     const char *username;
     const char *domain;
+    struct sss_domain_info *domain_info;
+    /* Number of groups */
     uint32_t gnum;
-    uint32_t *groups;
+    /* Group IDs */
+    uint32_t *gids;
+    /* Group output names */
+    char **gnames;
 };
 
-static struct dp_initgr_ctx *create_initgr_ctx(TALLOC_CTX *mem_ctx,
-                                               const char *domain,
-                                               struct ldb_result *res)
+static struct dp_initgr_ctx *create_initgr_ctx(
+                                        TALLOC_CTX *mem_ctx,
+                                        const char *domain,
+                                        struct sss_domain_info *domain_info,
+                                        struct ldb_result *res)
 {
     struct dp_initgr_ctx *ctx;
     const char *username;
+    const char *groupname;
     unsigned int i;
     errno_t ret;
 
@@ -105,27 +115,68 @@ static struct dp_initgr_ctx *create_initgr_ctx(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    /* Copy domain name */
     ctx->domain = talloc_strdup(ctx, domain);
     if (ctx->domain == NULL) {
         ret = ENOMEM;
         goto done;
     }
 
-    ctx->groups = talloc_array(mem_ctx, uint32_t, res->count);
-    if (ctx->groups == NULL) {
+    /* Reference domain info */
+    ctx->domain_info = domain_info;
+
+    /* Copy DN */
+    ctx->dn = ldb_dn_copy(ctx, res->msgs[0]->dn);
+    if (ctx->dn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /*
+     * Copy group IDs and names
+     */
+    ctx->gids = talloc_array(ctx, uint32_t, res->count);
+    if (ctx->gids == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    ctx->gnames = talloc_array(ctx, char *, res->count);
+    if (ctx->gnames == NULL) {
         ret = ENOMEM;
         goto done;
     }
 
     /* The first GID is the primary so it might be duplicated
      * later in the list. */
+    /* FIXME Do we need to/can we retrieve the primary group name? */
     for (ctx->gnum = 0, i = 0; i < res->count; i++) {
-        ctx->groups[ctx->gnum] = ldb_msg_find_attr_as_uint(res->msgs[i],
+        /* Copy GID */
+        ctx->gids[ctx->gnum] = ldb_msg_find_attr_as_uint(res->msgs[i],
                                                            SYSDB_GIDNUM, 0);
+
         /* If 0 it may be a non-posix group, so we skip it. */
-        if (ctx->groups[ctx->gnum] != 0) {
-            ctx->gnum++;
+        if (ctx->gids[ctx->gnum] == 0) {
+            continue;
+        }
+
+        /* Get output name */
+        groupname = (ctx->gnum == 0)
+                        ? NULL
+                        : sss_nss_get_name_from_msg(ctx->domain_info,
+                                                    res->msgs[i]);
+        if (groupname == NULL) {
+            ctx->gnames[ctx->gnum] = NULL;
+        } else {
+            /* TODO Get actual override_space */
+            ret = sss_nss_output_name(ctx, ctx->domain_info, groupname, '_',
+                                      &ctx->gnames[ctx->gnum]);
+            if (ret != EOK) {
+                goto done;
+            }
         }
+
+        /* Proceed */
+        ctx->gnum++;
     }
 
     ret = EOK;
@@ -139,10 +190,10 @@ static struct dp_initgr_ctx *create_initgr_ctx(TALLOC_CTX *mem_ctx,
     return ctx;
 }
 
-static void dp_req_initgr_pp(const char *req_name,
-                             struct data_provider *provider,
-                             struct dp_initgr_ctx *ctx,
-                             struct dp_reply_std *reply)
+static void dp_req_initgr_pp_nss_notify(const char *req_name,
+                                        struct data_provider *provider,
+                                        struct dp_initgr_ctx *ctx,
+                                        struct dp_reply_std *reply)
 {
     struct dp_client *dp_cli;
     DBusMessage *msg;
@@ -168,7 +219,7 @@ static void dp_req_initgr_pp(const char *req_name,
                                      DBUS_TYPE_STRING, &ctx->username,
                                      DBUS_TYPE_STRING, &ctx->domain,
                                      DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
-                                     &ctx->groups, num,
+                                     &ctx->gids, num,
                                      DBUS_TYPE_INVALID);
     if (!dbret) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
@@ -185,6 +236,118 @@ static void dp_req_initgr_pp(const char *req_name,
     return;
 }
 
+static void dp_req_initgr_pp_sr_overlay(const char *req_name,
+                                        struct data_provider *provider,
+                                        struct dp_initgr_ctx *ctx,
+                                        struct dp_reply_std *reply)
+{
+    bool enabled = false;
+    char *output_username;
+    char **conf_user;
+    char **conf_group;
+    size_t i;
+    TALLOC_CTX *tmp_ctx = NULL;
+    int ret;
+    struct ldb_message_element el = { 0, SYSDB_SESSION_RECORDING, 0, NULL };
+    struct sysdb_attrs del_attrs = { 1, &el };
+    struct sysdb_attrs *add_attrs;
+
+    (void)req_name;
+    (void)reply;
+
+    if (provider->be_ctx->sr_conf.scope != SESSION_RECORDING_CONF_SCOPE_SOME) {
+        goto done;
+    }
+
+    /* Delete sessionRecording attribute so we know when we failed */
+    ret = sysdb_set_entry_attr(ctx->domain_info->sysdb, ctx->dn,
+                               &del_attrs, SYSDB_MOD_DEL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed removing %s attribute: %s\n",
+              SYSDB_SESSION_RECORDING, sss_strerror(ret));
+        goto done;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed creating temporary talloc context\n");
+        goto done;
+    }
+
+    /* Format output username */
+    /* TODO Get actual override_space */
+    ret = sss_nss_output_name(tmp_ctx, ctx->domain_info, ctx->username, '_',
+                              &output_username);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed formatting output username from %s: %s\n",
+              ctx->username, sss_strerror(ret));
+        goto done;
+    }
+
+    /* For each user name in session recording config */
+    conf_user = provider->be_ctx->sr_conf.users;
+    if (conf_user != NULL) {
+        for (; *conf_user != NULL && !enabled; conf_user++) {
+            /* If it matches the requested user name */
+            if (strcmp(*conf_user, output_username) == 0) {
+                enabled = true;
+            }
+        }
+    }
+
+    /* For each group in configuration */
+    conf_group = provider->be_ctx->sr_conf.groups;
+    if (conf_group != NULL) {
+        for (; *conf_group != NULL && !enabled; conf_group++) {
+            /* For each group in response */
+            for (i = 0; i < ctx->gnum && !enabled; i++) {
+                if (ctx->gnames[i] != NULL &&
+                    strcmp(*conf_group, ctx->gnames[i]) == 0) {
+                    enabled = true;
+                }
+            }
+        }
+    }
+
+    /* Set sessionRecording attribute to enabled value */
+    add_attrs = sysdb_new_attrs(tmp_ctx);
+    if (add_attrs == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed creating attributes\n");
+        goto done;
+    }
+    ret = sysdb_attrs_add_bool(add_attrs, SYSDB_SESSION_RECORDING, enabled);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed setting %s attribute: %s\n",
+              SYSDB_SESSION_RECORDING, sss_strerror(ret));
+        goto done;
+    }
+    ret = sysdb_set_entry_attr(ctx->domain_info->sysdb, ctx->dn,
+                               add_attrs, SYSDB_MOD_ADD);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed storing %s attribute: %s\n",
+              SYSDB_SESSION_RECORDING, sss_strerror(ret));
+        goto done;
+    }
+
+done:
+    talloc_free(tmp_ctx);
+}
+
+static void dp_req_initgr_pp(const char *req_name,
+                             struct data_provider *provider,
+                             struct dp_initgr_ctx *ctx,
+                             struct dp_reply_std *reply)
+{
+    dp_req_initgr_pp_nss_notify(req_name, provider, ctx, reply);
+    dp_req_initgr_pp_sr_overlay(req_name, provider, ctx, reply);
+}
+
 static errno_t dp_initgroups(struct sbus_request *sbus_req,
                              struct dp_client *dp_cli,
                              const char *key,
@@ -218,7 +381,7 @@ static errno_t dp_initgroups(struct sbus_request *sbus_req,
         goto done;
     }
 
-    ctx = create_initgr_ctx(sbus_req, data->domain, res);
+    ctx = create_initgr_ctx(sbus_req, data->domain, domain, res);
     if (ctx == NULL) {
         ret = ENOMEM;
         goto done;

From 02d979effc4379e9da77c11bc89a7c67df9cde6c Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Tue, 21 Mar 2017 11:30:20 +0200
Subject: [PATCH 09/11] CACHE_REQ: Pull sessionRecording attrs from initgr

After entires are retrieved by cache_req for user info requests (except
initgr), overlay them with sessionRecording attribute retrieved from an
initgr request made additionally for each entry.
---
 src/responder/common/cache_req/cache_req.c | 165 +++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index aca150d..dbfa99b 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -613,6 +613,8 @@ struct cache_req_state {
     struct cache_req_result **results;
     size_t num_results;
     bool first_iteration;
+    size_t sr_overlay_res_idx;
+    size_t sr_overlay_msg_idx;
 };
 
 static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
@@ -842,6 +844,164 @@ static errno_t cache_req_search_domains(struct tevent_req *req,
     return EAGAIN;
 }
 
+static struct tevent_req *cache_req_sr_overlay_step_send(
+                                            struct cache_req_state *state)
+{
+    struct cache_req *cr = state->cr;
+    const char *name;
+
+    /* TODO Retrieve the name properly */
+    name = ldb_msg_find_attr_as_string(state->
+                                           results[state->sr_overlay_res_idx]->
+                                               msgs[state->sr_overlay_msg_idx],
+                                       SYSDB_NAME, NULL);
+
+    return cache_req_initgr_by_name_send(state,
+                                         state->ev,
+                                         cr->rctx,
+                                         cr->ncache,
+                                         cr->midpoint,
+                                         state->domain_name,
+                                         name);
+}
+
+static void cache_req_sr_overlay_step_done(struct tevent_req *subreq)
+{
+    int lret;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct tevent_req *req;
+    struct cache_req_state *state;
+    struct cache_req_result *result;
+    struct ldb_message *msg;
+    const char *enabled;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct cache_req_state);
+    msg = state->results[state->sr_overlay_res_idx]->
+                    msgs[state->sr_overlay_msg_idx];
+
+    /* Crete temporary allocation context */
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                        "Failed creating temporary talloc context\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Get initgroups result */
+    ret = cache_req_initgr_by_name_recv(tmp_ctx, subreq, &result);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                        "Failed retrieving initgr request results: %s\n",
+                        sss_strerror(ret));
+        goto done;
+    }
+
+    /* Overwrite sessionRecording attribute */
+    ldb_msg_remove_attr(msg, SYSDB_SESSION_RECORDING);
+    enabled = ldb_msg_find_attr_as_string(result->msgs[0],
+                                          SYSDB_SESSION_RECORDING, NULL);
+    if (enabled != NULL) {
+        char *enabled_copy;
+        enabled_copy = talloc_strdup(tmp_ctx, enabled);
+        if (enabled_copy == NULL) {
+            CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                            "Failed to allocate a copy of %s attribute\n",
+                            SYSDB_SESSION_RECORDING);
+            ret = ENOMEM;
+            goto done;
+        }
+        lret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING, enabled_copy);
+        if (lret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(lret);
+            CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                            "Failed retrieving initgroups request results\n");
+            goto done;
+        }
+        talloc_steal(msg, enabled_copy);
+    }
+
+    /* Move onto next entry, if any */
+    state->sr_overlay_msg_idx++;
+    if (state->sr_overlay_msg_idx >=
+            state->results[state->sr_overlay_res_idx]->count) {
+        state->sr_overlay_res_idx++;
+        if (state->sr_overlay_res_idx >= state->num_results) {
+            ret = EOK;
+            goto done;
+        }
+        state->sr_overlay_msg_idx = 0;
+    }
+
+    /* Schedule next entry overlay */
+    subreq = cache_req_sr_overlay_step_send(state);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                        "Failed allocating a session recording "
+                        "overlay request\n");
+        goto done;
+    }
+    tevent_req_set_callback(subreq,
+                            cache_req_sr_overlay_step_done, req);
+    ret = EAGAIN;
+
+done:
+    if (ret == EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Success\n");
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                        "Finished: Error %d: %s\n", ret, sss_strerror(ret));
+        tevent_req_error(req, ret);
+    }
+    talloc_free(tmp_ctx);
+}
+
+static errno_t cache_req_sr_overlay(struct tevent_req *req)
+{
+    struct cache_req_state *state;
+    struct cache_req *cr;
+    struct resp_ctx *rctx;
+    struct tevent_req *subreq;
+
+    state = tevent_req_data(req, struct cache_req_state);
+    cr = state->cr;
+    rctx = cr->rctx;
+
+    /* Don't do anything if session recording is not selective */
+    if (rctx->sr_conf.scope != SESSION_RECORDING_CONF_SCOPE_SOME) {
+        return EOK;
+    }
+
+    /* Only handle requests for users */
+    switch (cr->data->type) {
+        case CACHE_REQ_USER_BY_NAME:
+        case CACHE_REQ_USER_BY_UPN:
+        case CACHE_REQ_USER_BY_ID:
+        case CACHE_REQ_ENUM_USERS:
+            break;
+        default:
+            return EOK;
+    }
+
+    /* Schedule first entry overlay */
+    subreq = cache_req_sr_overlay_step_send(state);
+    if (subreq == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                        "Failed allocating a session recording "
+                        "overlay request\n");
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq,
+                            cache_req_sr_overlay_step_done, req);
+
+    return EAGAIN;
+}
+
 static void cache_req_done(struct tevent_req *subreq)
 {
     struct cache_req_state *state;
@@ -876,6 +1036,11 @@ static void cache_req_done(struct tevent_req *subreq)
         }
     }
 
+    /* Overlay with session recording flag, if needed */
+    if (ret == EOK) {
+        ret = cache_req_sr_overlay(req);
+    }
+
     switch (ret) {
     case EOK:
         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Success\n");

From 83b8b498486d5b63b508c74f65709d802bb4a2b0 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Tue, 21 Mar 2017 11:45:37 +0200
Subject: [PATCH 10/11] NSS: Substitute session recording shell

Substitute the configured session recording shell when unconditional
session recording is enabled (scope = all), or when selective session
recording is enabled (scope = some), and the user has the
sessionRecording attribute set to true.
---
 src/responder/nss/nss_protocol_pwent.c | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
index c0b8e79..7ea987c 100644
--- a/src/responder/nss/nss_protocol_pwent.c
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -239,7 +239,32 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
     gecos = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_GECOS,
                                                  NULL);
     homedir = nss_get_homedir(mem_ctx, nss_ctx, domain, msg, name, upn, uid);
-    shell = nss_get_shell_override(msg, nss_ctx, domain);
+    shell = NULL;
+    if (nss_ctx->rctx->sr_conf.scope == SESSION_RECORDING_CONF_SCOPE_ALL) {
+        shell = SESSION_RECORDING_SHELL;
+    } else if (nss_ctx->rctx->sr_conf.scope ==
+               SESSION_RECORDING_CONF_SCOPE_SOME) {
+        const char *sr_enabled;
+        sr_enabled = ldb_msg_find_attr_as_string(
+                                    msg, SYSDB_SESSION_RECORDING, NULL);
+        if (sr_enabled == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "%s attribute not found for %s[%u]! Skipping\n",
+                  SYSDB_SESSION_RECORDING, name, uid);
+            return EINVAL;
+        } else if (strcmp(sr_enabled, "TRUE") == 0) {
+            shell = SESSION_RECORDING_SHELL;
+        } else if (strcmp(sr_enabled, "FALSE") != 0) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Skipping %s[%u] "
+                  "because its %s attribute value is invalid: %s\n",
+                  name, uid, SYSDB_SESSION_RECORDING, sr_enabled);
+            return EINVAL;
+        }
+    }
+    if (shell == NULL) {
+        shell = nss_get_shell_override(msg, nss_ctx, domain);
+    }
 
     /* Convert to sized strings. */
     ret = sized_output_name(mem_ctx, nss_ctx->rctx, name, domain, _name);

From d0e3f2221b21f974e3b87e5e1245eea75277028f Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 11 Aug 2016 14:18:38 +0300
Subject: [PATCH 11/11] INTG: Add session recording tests

---
 src/tests/intg/Makefile.am         |   1 +
 src/tests/intg/config.py.m4        |  29 ++---
 src/tests/intg/test_enumeration.py | 251 +++++++++++++++++++++++++++++++++++++
 3 files changed, 267 insertions(+), 14 deletions(-)

diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index 1d36fa0..576a68d 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -39,6 +39,7 @@ config.py: config.py.m4
 	   -D "libexecpath=\`$(libexecdir)'" \
 	   -D "runstatedir=\`$(runstatedir)'" \
 	   -D "abs_builddir=\`$(abs_builddir)'" \
+	   -D "session_recording_shell=\`$(session_recording_shell)'" \
 	   $< > $@
 
 root:
diff --git a/src/tests/intg/config.py.m4 b/src/tests/intg/config.py.m4
index 841aae0..bfbbf03 100644
--- a/src/tests/intg/config.py.m4
+++ b/src/tests/intg/config.py.m4
@@ -2,17 +2,18 @@
 Build configuration variables.
 """
 
-PREFIX              = "prefix"
-SYSCONFDIR          = "sysconfdir"
-NSS_MODULE_DIR      = PREFIX + "/lib"
-SSSDCONFDIR         = SYSCONFDIR + "/sssd"
-CONF_PATH           = SSSDCONFDIR + "/sssd.conf"
-DB_PATH             = "dbpath"
-PID_PATH            = "pidpath"
-PIDFILE_PATH        = PID_PATH + "/sssd.pid"
-LOG_PATH            = "logpath"
-MCACHE_PATH         = "mcpath"
-SECDB_PATH          = "secdbpath"
-LIBEXEC_PATH        = "libexecpath"
-RUNSTATEDIR         = "runstatedir"
-ABS_BUILDDIR        = "abs_builddir"
+PREFIX                  = "prefix"
+SYSCONFDIR              = "sysconfdir"
+NSS_MODULE_DIR          = PREFIX + "/lib"
+SSSDCONFDIR             = SYSCONFDIR + "/sssd"
+CONF_PATH               = SSSDCONFDIR + "/sssd.conf"
+DB_PATH                 = "dbpath"
+PID_PATH                = "pidpath"
+PIDFILE_PATH            = PID_PATH + "/sssd.pid"
+LOG_PATH                = "logpath"
+MCACHE_PATH             = "mcpath"
+SECDB_PATH              = "secdbpath"
+LIBEXEC_PATH            = "libexecpath"
+RUNSTATEDIR             = "runstatedir"
+ABS_BUILDDIR            = "abs_builddir"
+SESSION_RECORDING_SHELL = "session_recording_shell"
diff --git a/src/tests/intg/test_enumeration.py b/src/tests/intg/test_enumeration.py
index 47772de..9648e08 100644
--- a/src/tests/intg/test_enumeration.py
+++ b/src/tests/intg/test_enumeration.py
@@ -695,3 +695,254 @@ def test_vetoed_shells(vetoed_shells):
                  shell="/bin/default")
         )
     )
+
+
+@pytest.fixture
+def session_recording_ldap(request, ldap_conn):
+    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
+    ent_list.add_user("user1", 1001, 2001, loginShell="/bin/sh1")
+    ent_list.add_user("user2", 1002, 2002, loginShell="/bin/sh2")
+    ent_list.add_user("user3", 1003, 2003, loginShell="/bin/sh3")
+    ent_list.add_group("group1", 2001)
+    ent_list.add_group("group2", 2002)
+    ent_list.add_group("group3", 2003)
+    ent_list.add_group("empty_group", 2010)
+    ent_list.add_group("one_user_group", 2011, ["user1"])
+    ent_list.add_group("two_user_group", 2012, ["user1", "user2"])
+    ent_list.add_group("three_user_group", 2013, ["user1", "user2", "user3"])
+    create_ldap_fixture(request, ldap_conn, ent_list)
+
+
+@pytest.fixture
+def session_recording_none(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = none
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_none(session_recording_none):
+    """Test session recording "none" scope"""
+    ent.assert_passwd(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell="/bin/sh1"),
+            dict(name="user2", uid=1002, shell="/bin/sh2"),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_all(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = all
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_all_nam(session_recording_all):
+    """Test session recording "all" scope with getpwnam"""
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    ))
+    # Request a second time to verify positive caching
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    ))
+
+
+def test_session_recording_all_uid(session_recording_all):
+    """Test session recording "all" scope with getpwuid"""
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    })
+    # Request a second time to verify positive caching
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    })
+
+
+def test_session_recording_all_ent(session_recording_all):
+    """Test session recording "all" scope with getpwent"""
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_empty(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_empty(session_recording_some_empty):
+    """Test session recording "some" scope with no users or groups"""
+    ent.assert_passwd(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell="/bin/sh1"),
+            dict(name="user2", uid=1002, shell="/bin/sh2"),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_users(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+            users = user1, user2
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_users_nam(session_recording_some_users):
+    """Test session recording "some" scope with user list and getpwnam"""
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell="/bin/sh3"),
+    ))
+
+
+def test_session_recording_some_users_uid(session_recording_some_users):
+    """Test session recording "some" scope with user list and getpwuid"""
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell="/bin/sh3"),
+    })
+
+
+def test_session_recording_some_users_ent(session_recording_some_users):
+    """Test session recording "some" scope with user list and getpwent"""
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_groups(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+            groups = one_user_group, two_user_group
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_groups_nam(session_recording_some_groups):
+    """Test session recording "some" scope with group list and getpwnam"""
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell="/bin/sh3"),
+    ))
+
+
+def test_session_recording_some_groups_uid(session_recording_some_groups):
+    """Test session recording "some" scope with group list and getpwuid"""
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell="/bin/sh3"),
+    })
+
+
+def test_session_recording_some_groups_ent(session_recording_some_groups):
+    """Test session recording "some" scope with group list and getpwent"""
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_users_and_groups(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+            users = user3
+            groups = one_user_group
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_users_and_groups_nam(
+                                    session_recording_some_users_and_groups):
+    """
+    Test session recording "some" scope with user and group lists and getpwnam
+    """
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell="/bin/sh2"),
+        user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    ))
+
+
+def test_session_recording_some_users_and_groups_uid(
+                                    session_recording_some_users_and_groups):
+    """
+    Test session recording "some" scope with user and group lists and getpwuid
+    """
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell="/bin/sh2"),
+        1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    })
+
+
+def test_session_recording_some_users_and_groups_ent(
+                                    session_recording_some_users_and_groups):
+    """
+    Test session recording "some" scope with user and group lists and getpwent
+    """
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell="/bin/sh2"),
+            dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+        )
+    )
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org

Reply via email to