URL: https://github.com/SSSD/sssd/pull/136
Author: spbnick
 Title: #136: Tlog integration
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 871c2b596de3ae5238750ba6bd4a008a40e98138 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 24 Mar 2017 16:24:22 +0200
Subject: [PATCH 01/14] CACHE_REQ: Propagate num_results to cache_req_state

The num_results field in struct cache_req_state was only set in case of
well-known objects, set it also for the regular results for uniformity,
and for later use by session recording code.
---
 src/responder/common/cache_req/cache_req.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index aca150d..077282e 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -536,7 +536,8 @@ static void cache_req_search_domains_done(struct tevent_req *subreq)
 static errno_t
 cache_req_search_domains_recv(TALLOC_CTX *mem_ctx,
                               struct tevent_req *req,
-                              struct cache_req_result ***_results)
+                              struct cache_req_result ***_results,
+                              size_t *_num_results)
 {
     struct cache_req_search_domains_state *state;
 
@@ -547,6 +548,9 @@ cache_req_search_domains_recv(TALLOC_CTX *mem_ctx,
     if (_results != NULL) {
         *_results = talloc_steal(mem_ctx, state->results);
     }
+    if (_num_results != NULL) {
+        *_num_results = state->num_results;
+    }
 
     return EOK;
 }
@@ -851,7 +855,8 @@ static void cache_req_done(struct tevent_req *subreq)
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct cache_req_state);
 
-    ret = cache_req_search_domains_recv(state, subreq, &state->results);
+    ret = cache_req_search_domains_recv(state, subreq,
+                                        &state->results, &state->num_results);
     talloc_zfree(subreq);
 
     if (ret == ENOENT && state->first_iteration) {

From 81d03785289ba25615a68a75d74c9d6051e19392 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Wed, 22 Mar 2017 14:32:35 +0200
Subject: [PATCH 02/14] 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_protocol_sid.c   |  3 +-
 src/responder/nss/nss_utils.c          | 55 +++++----------------------
 src/util/sss_nss.c                     | 68 ++++++++++++++++++++++++++++++++++
 src/util/sss_nss.h                     | 12 ++++++
 6 files changed, 94 insertions(+), 49 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_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c
index a6a4e27..37bbec5 100644
--- a/src/responder/nss/nss_protocol_sid.c
+++ b/src/responder/nss/nss_protocol_sid.c
@@ -19,6 +19,7 @@
 */
 
 #include "util/crypto/sss_crypto.h"
+#include "util/sss_nss.h"
 #include "responder/nss/nss_protocol.h"
 
 static errno_t
@@ -532,7 +533,7 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx,
             return ret;
         }
 
-        tmp_str = nss_get_name_from_msg(result->domain, result->msgs[c]);
+        tmp_str = sss_nss_get_name_from_msg(result->domain, result->msgs[c]);
         if (tmp_str == NULL) {
             return EINVAL;
         }
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..0ff8c10 100644
--- a/src/util/sss_nss.h
+++ b/src/util/sss_nss.h
@@ -22,9 +22,11 @@
 #ifndef __SSS_NSS_H__
 #define __SSS_NSS_H__
 
+#include "confdb/confdb.h"
 #include <stdbool.h>
 #include <sys/types.h>
 #include <talloc.h>
+#include <ldb.h>
 
 struct sss_nss_homedir_ctx {
     const char *username;
@@ -39,4 +41,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 0e8e6bd0fddfa3608aaeb622d2a3b60635259e09 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Tue, 20 Dec 2016 10:16:47 +0200
Subject: [PATCH 03/14] 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 fb60675..5fa1019 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 5e9c78722b7bb3855544018ef76c31766ca1e774 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Thu, 11 Aug 2016 14:15:55 +0300
Subject: [PATCH 04/14] 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 cf5e255..51169ce 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 4209972..56e7778 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 76658abedd80afceeaafdef60056b31c17be434d Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 17 Mar 2017 12:41:02 +0200
Subject: [PATCH 05/14] 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 91afdd6..96ed3ef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -650,6 +650,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 <[email protected]>
+
+    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 <[email protected]>
+
+    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 15ab1cd90a8965dcad361f87fe28de6ee88f39be Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Thu, 12 Jan 2017 19:10:25 +0200
Subject: [PATCH 06/14] 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 96ed3ef..64f1823 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -571,6 +571,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)
@@ -2199,6 +2200,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 = \
@@ -2287,6 +2289,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 c92bf437ab29c667601b631447c738fee9938899 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 17 Mar 2017 12:33:53 +0200
Subject: [PATCH 07/14] 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 64f1823..da11046 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1562,6 +1562,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 10fdcf857f468c34bb9e9676e39079dea6e7a3c5 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 17 Mar 2017 12:35:51 +0200
Subject: [PATCH 08/14] 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 0cbb2c5..445678f 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -184,6 +184,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"=*)"
 
@@ -236,6 +237,7 @@
                         SYSDB_OVERRIDE_DN, \
                         SYSDB_OVERRIDE_OBJECT_DN, \
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
+                        SYSDB_SESSION_RECORDING, \
                         SYSDB_UUID, \
                         SYSDB_ORIG_DN, \
                         NULL}

From d0c61bcea01f19834ab1069a17af59216cc2ec30 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 24 Mar 2017 16:42:21 +0200
Subject: [PATCH 09/14] DP: Load override_space into be_ctx

Add loading and storing the override_space configuration option to data
provider. That will be needed for formatting output user and group names
for matching against session recording configuration.
---
 src/providers/backend.h          |  1 +
 src/providers/data_provider_be.c | 22 ++++++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/src/providers/backend.h b/src/providers/backend.h
index e6d39ef..4e68a80 100644
--- a/src/providers/backend.h
+++ b/src/providers/backend.h
@@ -84,6 +84,7 @@ struct be_ctx {
     const char *conf_path;
     uid_t uid;
     gid_t gid;
+    char override_space;
     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 f126ab0..070ec9a 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -376,6 +376,7 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
     uint32_t refresh_interval;
     struct tevent_signal *tes;
     struct be_ctx *be_ctx;
+    char *str = NULL;
     errno_t ret;
 
     be_ctx = talloc_zero(mem_ctx, struct be_ctx);
@@ -427,6 +428,27 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    /* Read the global override_space option, for output name formatting */
+    ret = confdb_get_string(cdb, be_ctx, CONFDB_MONITOR_CONF_ENTRY,
+                            CONFDB_MONITOR_OVERRIDE_SPACE, NULL,
+                            &str);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannnot get the space substitution character [%d]: %s\n",
+               ret, strerror(ret));
+        goto done;
+    }
+
+    if (str != NULL) {
+        if (strlen(str) > 1) {
+            DEBUG(SSSDBG_MINOR_FAILURE, "Option %s is longer than 1 character "
+                  "only the first character %c will be used\n",
+                  CONFDB_MONITOR_OVERRIDE_SPACE, str[0]);
+        }
+
+        be_ctx->override_space = str[0];
+    }
+
     /* Read session_recording section */
     ret = session_recording_conf_load(be_ctx, cdb, &be_ctx->sr_conf);
     if (ret != EOK) {

From 4a2ad434f8a13a99210649764d105bef8a945f24 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 17 Mar 2017 12:34:58 +0200
Subject: [PATCH 10/14] 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 | 206 ++++++++++++++++++++++++++---
 1 file changed, 191 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..ffceaa2 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,67 @@ 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;
+        }
+
+        /* Copy group 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 {
+            ctx->gnames[ctx->gnum] = talloc_strdup(ctx, groupname);
+            if (ctx->gnames[ctx->gnum] == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
         }
+
+        /* Proceed */
+        ctx->gnum++;
     }
 
     ret = EOK;
@@ -139,10 +189,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 +218,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 +235,132 @@ 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;
+    struct be_ctx *be = provider->be_ctx;
+    char *output_name;
+    char **conf_user;
+    char **conf_group;
+    size_t i;
+    TALLOC_CTX *tmp_ctx = NULL;
+    errno_t 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 (be->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");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Format output username */
+    ret = sss_nss_output_name(tmp_ctx, ctx->domain_info, ctx->username,
+                              be->override_space, &output_name);
+    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 = be->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_name) == 0) {
+                enabled = true;
+            }
+        }
+    }
+
+    /* If we have groups in config and are not yet enabled */
+    conf_group = be->sr_conf.groups;
+    if (conf_group != NULL && *conf_group != NULL && !enabled) {
+        /* For each group name in response */
+        for (i = 0; i < ctx->gnum && !enabled; i++) {
+            if (ctx->gnames[i] != NULL) {
+                /* Get the output group name */
+                ret = sss_nss_output_name(tmp_ctx, ctx->domain_info,
+                                          ctx->gnames[i], be->override_space,
+                                          &output_name);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Failed formatting output group name from %s: %s\n",
+                          ctx->gnames[i], sss_strerror(ret));
+                    goto done;
+                }
+                /* For each group in configuration */
+                for (; *conf_group != NULL && !enabled; conf_group++) {
+                    if (strcmp(*conf_group, output_name) == 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");
+        ret = ENOMEM;
+        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 +394,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 c6aee5fbe21bb56a5f125c37f163b067025adb24 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Tue, 21 Mar 2017 11:30:20 +0200
Subject: [PATCH 11/14] 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 | 159 +++++++++++++++++++++++++++++
 1 file changed, 159 insertions(+)

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index 077282e..85e4aae 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -617,6 +617,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,
@@ -846,6 +848,158 @@ 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;
+    struct cache_req_result *result =
+                                state->results[state->sr_overlay_res_idx];
+    const char *name;
+
+    name = ldb_msg_find_attr_as_string(result->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, NULL, 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 adding %s attribute: %s\n",
+                            SYSDB_SESSION_RECORDING, sss_strerror(ret));
+            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;
@@ -881,6 +1035,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 d6b7d55941b6330de1bb4ad0514c74fe96fe0ec5 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Tue, 21 Mar 2017 11:45:37 +0200
Subject: [PATCH 12/14] 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 bff9ebb1aba51a9457ee6f61847b74d61f5effc9 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Thu, 11 Aug 2016 14:18:38 +0300
Subject: [PATCH 13/14] INTG: Add session recording tests

Add basic tests for all base combinations of session recording
configuration options.
---
 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 8526bea..3c4c15f 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -42,6 +42,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")
         )
     )
+
+
[email protected]
+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)
+
+
[email protected]
+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"),
+        )
+    )
+
+
[email protected]
+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),
+        )
+    )
+
+
[email protected]
+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"),
+        )
+    )
+
+
[email protected]
+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"),
+        )
+    )
+
+
[email protected]
+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"),
+        )
+    )
+
+
[email protected]
+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),
+        )
+    )

From 83124ef5b380a6dc6a2c2b4204e608b5b12adb32 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <[email protected]>
Date: Fri, 24 Mar 2017 16:30:29 +0200
Subject: [PATCH 14/14] CACHE_REQ: Avoid unnecessary initgr request

Do not do additional initgr requests with selective session recording
enabled, if we don't have any group names to match against in session
recording configuration. Only do user name matches instead.
---
 src/responder/common/cache_req/cache_req.c | 145 +++++++++++++++++++++++++----
 1 file changed, 129 insertions(+), 16 deletions(-)

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index 85e4aae..25f7da5 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 
 #include "util/util.h"
+#include "util/sss_nss.h"
 #include "responder/common/cache_req/cache_req_private.h"
 #include "responder/common/cache_req/cache_req_plugin.h"
 
@@ -848,7 +849,7 @@ static errno_t cache_req_search_domains(struct tevent_req *req,
     return EAGAIN;
 }
 
-static struct tevent_req *cache_req_sr_overlay_step_send(
+static struct tevent_req *cache_req_sr_overlay_match_all_step_send(
                                             struct cache_req_state *state)
 {
     struct cache_req *cr = state->cr;
@@ -862,7 +863,7 @@ static struct tevent_req *cache_req_sr_overlay_step_send(
                                          cr->midpoint, NULL, name);
 }
 
-static void cache_req_sr_overlay_step_done(struct tevent_req *subreq)
+static void cache_req_sr_overlay_match_all_step_done(struct tevent_req *subreq)
 {
     int lret;
     errno_t ret;
@@ -935,7 +936,7 @@ static void cache_req_sr_overlay_step_done(struct tevent_req *subreq)
     }
 
     /* Schedule next entry overlay */
-    subreq = cache_req_sr_overlay_step_send(state);
+    subreq = cache_req_sr_overlay_match_all_step_send(state);
     if (subreq == NULL) {
         ret = ENOMEM;
         CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
@@ -944,7 +945,7 @@ static void cache_req_sr_overlay_step_done(struct tevent_req *subreq)
         goto done;
     }
     tevent_req_set_callback(subreq,
-                            cache_req_sr_overlay_step_done, req);
+                            cache_req_sr_overlay_match_all_step_done, req);
     ret = EAGAIN;
 
 done:
@@ -959,12 +960,128 @@ static void cache_req_sr_overlay_step_done(struct tevent_req *subreq)
     talloc_free(tmp_ctx);
 }
 
+static errno_t cache_req_sr_overlay_match_all(struct tevent_req *req)
+{
+    struct cache_req_state *state;
+    struct tevent_req *subreq;
+
+    state = tevent_req_data(req, struct cache_req_state);
+
+    /* Schedule first entry overlay */
+    subreq = cache_req_sr_overlay_match_all_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_match_all_step_done, req);
+
+    return EAGAIN;
+}
+
+static errno_t cache_req_sr_overlay_match_users(struct tevent_req *req)
+{
+    struct cache_req_state *state;
+    struct cache_req *cr;
+    struct resp_ctx *rctx;
+    errno_t ret;
+    int lret;
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct cache_req_result *result;
+    struct ldb_message *msg;
+    const char *name;
+    char *output_name;
+    char **conf_user;
+    bool enabled;
+    char *enabled_str;
+
+    state = tevent_req_data(req, struct cache_req_state);
+    cr = state->cr;
+    rctx = cr->rctx;
+
+    /* For each result */
+    for (state->sr_overlay_res_idx = 0;
+         state->sr_overlay_res_idx < state->num_results;
+         state->sr_overlay_res_idx++) {
+        result = state->results[state->sr_overlay_res_idx];
+
+        /* For each message */
+        for (state->sr_overlay_msg_idx = 0;
+             state->sr_overlay_msg_idx < result->count;
+             state->sr_overlay_msg_idx++) {
+            msg = result->msgs[state->sr_overlay_msg_idx];
+
+            /* Create per-message talloc context */
+            tmp_ctx = talloc_new(NULL);
+            if (tmp_ctx == NULL) {
+                CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                                "Failed creating temporary talloc context\n");
+                ret = ENOMEM;
+                goto done;
+            }
+
+            /* Format output username */
+            name = sss_nss_get_name_from_msg(result->domain, msg);
+            ret = sss_nss_output_name(tmp_ctx, result->domain, name,
+                                      rctx->override_space,
+                                      &output_name);
+            if (ret != EOK) {
+                CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                                "Failed formatting output username from %s: %s\n",
+                                name, sss_strerror(ret));
+                goto done;
+            }
+
+            /* For each user name in session recording config */
+            enabled = false;
+            conf_user = rctx->sr_conf.users;
+            if (conf_user != NULL) {
+                for (; *conf_user != NULL; conf_user++) {
+                    /* If it matches the requested user name */
+                    if (strcmp(*conf_user, output_name) == 0) {
+                        enabled = true;
+                        break;
+                    }
+                }
+            }
+
+            /* Set sessionRecording attribute to enabled value */
+            ldb_msg_remove_attr(msg, SYSDB_SESSION_RECORDING);
+            enabled_str = talloc_strdup(tmp_ctx, enabled ? "TRUE" : "FALSE");
+            if (enabled_str == NULL) {
+                CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                                "Failed to allocate a %s attribute value\n",
+                                SYSDB_SESSION_RECORDING);
+                ret = ENOMEM;
+                goto done;
+            }
+            lret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING, enabled_str);
+            if (lret != LDB_SUCCESS) {
+                ret = sysdb_error_to_errno(lret);
+                CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                                "Failed adding %s attribute: %s\n",
+                                SYSDB_SESSION_RECORDING, sss_strerror(ret));
+                goto done;
+            }
+            talloc_steal(msg, enabled_str);
+
+            /* Free per-message talloc context */
+            talloc_zfree(tmp_ctx);
+        }
+    }
+
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
 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;
@@ -986,18 +1103,14 @@ static errno_t cache_req_sr_overlay(struct tevent_req *req)
             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;
+    /* If we have group names to match against */
+    if (rctx->sr_conf.groups != NULL && *rctx->sr_conf.groups != NULL) {
+        /* Pull and match group and user names for each user entry */
+        return cache_req_sr_overlay_match_all(req);
+    } else {
+        /* Only match user names for each user entry */
+        return cache_req_sr_overlay_match_users(req);
     }
-    tevent_req_set_callback(subreq,
-                            cache_req_sr_overlay_step_done, req);
-
-    return EAGAIN;
 }
 
 static void cache_req_done(struct tevent_req *subreq)
_______________________________________________
sssd-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to