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 22256f94283bce43698b903f6ccb93e58031784c Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 24 Mar 2017 16:24:22 +0200
Subject: [PATCH 01/17] 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 4831263..96e7a26 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -548,7 +548,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;
 
@@ -559,6 +560,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;
 }
@@ -866,7 +870,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 842c010e7cf5bb2599fa851ec91ab3e5fd369da2 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 02/17] 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 2e07e523633f2f09db5a4c5466f2464e6fc2bd7a 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 03/17] 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 56a6036..8cf63a7 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -161,6 +161,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 6899bf8..8dfbff8 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -1361,7 +1361,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" %
@@ -1455,7 +1456,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 6965028..80e3aff 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -114,6 +114,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 c58a60caae22873706f88329167fadbec8fdeb5e 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 04/17] 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 c44256f88d0785bb4ed6b7f9cfb84ea91916a765 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 05/17] 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 573b37c..31f41b8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -652,6 +652,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 c6531ed8e25bbb041ed04e7b24d4e22a26df71b8 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 06/17] 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 31f41b8..8ee70ca 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -573,6 +573,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)
@@ -2205,6 +2206,7 @@ responder_socket_access_tests_SOURCES = \
     src/responder/common/cache_req/cache_req_domain.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 = \
@@ -2293,6 +2295,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 4210307..8ce8253 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -38,6 +38,7 @@
 #include "responder/common/negcache.h"
 #include "sss_client/sss_cli.h"
 #include "responder/common/cache_req/cache_req_domain.h"
+#include "util/session_recording_conf.h"
 
 extern hash_table_t *dp_requests;
 
@@ -135,6 +136,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 154d7dc..13b5fc8 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -1176,6 +1176,15 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
               ret, sss_strerror(ret));
     }
 
+    /* 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 c99ebde..e0fadf8 100644
--- a/src/tests/cwrap/Makefile.am
+++ b/src/tests/cwrap/Makefile.am
@@ -86,6 +86,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)
@@ -180,6 +181,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 6d57c4c1475586955ea3928e5bb1e27265c5bac8 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 07/17] 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 8ee70ca..42fdf8d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1567,6 +1567,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 3fff08907ae91aa9e4a27ea367ab1f04ffef26e0 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 08/17] 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 62c561b..78fbbc7 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -186,6 +186,7 @@
 #define SYSDB_OVERRIDE_OBJECT_DN "overrideObjectDN"
 #define SYSDB_USE_DOMAIN_RESOLUTION_ORDER "useDomainResolutionOrder"
 #define SYSDB_DOMAIN_RESOLUTION_ORDER "domainResolutionOrder"
+#define SYSDB_SESSION_RECORDING "sessionRecording"
 
 #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
 
@@ -238,6 +239,7 @@
                         SYSDB_OVERRIDE_DN, \
                         SYSDB_OVERRIDE_OBJECT_DN, \
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
+                        SYSDB_SESSION_RECORDING, \
                         SYSDB_UUID, \
                         SYSDB_ORIG_DN, \
                         NULL}

From 96ba1607b51294fb4834a12f7638af8b4f416b19 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 24 Mar 2017 16:42:21 +0200
Subject: [PATCH 09/17] 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 802789dbc5b295f4fbd0703ff03df4ced8a78e8a 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 10/17] 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 2d3e80d47825ef41ddc8ca7d66f9e38784c73e03 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 11/17] 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 96e7a26..e5dc16b 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -629,6 +629,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,
@@ -861,6 +863,158 @@ 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;
@@ -896,6 +1050,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 e33207aa7f980326306020a40728f66b948f2643 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 12/17] 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 546c97bed81558130e3321ae43e0bfc67b849c64 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 13/17] 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 8566106..703a1ca 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")
         )
     )
+
+
+@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),
+        )
+    )

From a50dc8a7ddf7867dc0959b1b972af2fca970925a Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Fri, 24 Mar 2017 16:30:29 +0200
Subject: [PATCH 14/17] 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 e5dc16b..a7f9017 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/responder.h"
 #include "responder/common/cache_req/cache_req_private.h"
 #include "responder/common/cache_req/cache_req_plugin.h"
@@ -863,7 +864,7 @@ 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;
@@ -877,7 +878,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;
@@ -950,7 +951,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,
@@ -959,7 +960,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:
@@ -974,12 +975,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;
@@ -1001,18 +1118,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)

From 3db0930ad1ee727d7089536c98cab6b71a95b401 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Wed, 29 Mar 2017 16:07:52 +0300
Subject: [PATCH 15/17] NSS: Move shell options to common responder

Move all the shell-related options from the NSS responder context to the
common responder context, so they can be used by other responders for
retrieving original user shell, when it is overrided for session
recording.
---
 src/responder/common/responder.h        |   7 +++
 src/responder/common/responder_common.c | 100 ++++++++++++++++++++++++++++++++
 src/responder/nss/nss_private.h         |   6 --
 src/responder/nss/nss_protocol_pwent.c  |  42 +++++++-------
 src/responder/nss/nsssrv.c              |  99 -------------------------------
 5 files changed, 128 insertions(+), 126 deletions(-)

diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 8ce8253..12d4e71 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -136,6 +136,13 @@ struct resp_ctx {
     char *default_domain;
     char override_space;
 
+    char **allowed_shells;
+    char *override_shell;
+    char **vetoed_shells;
+    char **etc_shells;
+    char *shell_fallback;
+    char *default_shell;
+
     struct session_recording_conf sr_conf;
 
     uint32_t cache_req_num;
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 13b5fc8..d363f3e 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -49,6 +49,9 @@
 #include <systemd/sd-daemon.h>
 #endif
 
+#define SHELL_REALLOC_INCREMENT 5
+#define SHELL_REALLOC_MAX       50
+
 static errno_t set_close_on_exec(int fd)
 {
     int v;
@@ -1037,6 +1040,72 @@ static errno_t responder_init_ncache(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
+static errno_t sss_get_etc_shells(TALLOC_CTX *mem_ctx, char ***_shells)
+{
+    int i = 0;
+    char *sh;
+    char **shells = NULL;
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    int size;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
+
+    shells = talloc_array(tmp_ctx, char *, SHELL_REALLOC_INCREMENT);
+    if (!shells) {
+        ret = ENOMEM;
+        goto done;
+    }
+    size = SHELL_REALLOC_INCREMENT;
+
+    setusershell();
+    while ((sh = getusershell())) {
+        shells[i] = talloc_strdup(shells, sh);
+        if (!shells[i]) {
+            endusershell();
+            ret = ENOMEM;
+            goto done;
+        }
+        DEBUG(SSSDBG_TRACE_FUNC, "Found shell %s in /etc/shells\n", shells[i]);
+        i++;
+
+        if (i == size) {
+            size += SHELL_REALLOC_INCREMENT;
+            if (size > SHELL_REALLOC_MAX) {
+                DEBUG(SSSDBG_FATAL_FAILURE,
+                      "Reached maximum number of shells [%d]. "
+                          "Users may be denied access. "
+                          "Please check /etc/shells for sanity\n",
+                          SHELL_REALLOC_MAX);
+                break;
+            }
+            shells = talloc_realloc(NULL, shells, char *,
+                                    size);
+            if (!shells) {
+                ret = ENOMEM;
+                goto done;
+            }
+        }
+    }
+    endusershell();
+
+    if (i + 1 < size) {
+        shells = talloc_realloc(NULL, shells, char *, i + 1);
+        if (!shells) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+    shells[i] = NULL;
+
+    *_shells = talloc_move(mem_ctx, &shells);
+    ret = EOK;
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
 int sss_process_init(TALLOC_CTX *mem_ctx,
                      struct tevent_context *ev,
                      struct confdb_ctx *cdb,
@@ -1176,6 +1245,37 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
               ret, sss_strerror(ret));
     }
 
+    /* Read shell settings */
+    ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+                            CONFDB_NSS_OVERRIDE_SHELL, NULL,
+                            &rctx->override_shell);
+    if (ret != EOK && ret != ENOENT) goto fail;
+
+    ret = confdb_get_string_as_list(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+                                    CONFDB_NSS_ALLOWED_SHELL,
+                                    &rctx->allowed_shells);
+    if (ret != EOK && ret != ENOENT) goto fail;
+
+    ret = confdb_get_string_as_list(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+                                    CONFDB_NSS_VETOED_SHELL,
+                                    &rctx->vetoed_shells);
+    if (ret != EOK && ret != ENOENT) goto fail;
+
+    ret = sss_get_etc_shells(rctx, &rctx->etc_shells);
+    if (ret != EOK) goto fail;
+
+    ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+                            CONFDB_NSS_SHELL_FALLBACK,
+                            CONFDB_DEFAULT_SHELL_FALLBACK,
+                            &rctx->shell_fallback);
+    if (ret != EOK) goto fail;
+
+    ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+                            CONFDB_NSS_DEFAULT_SHELL,
+                            NULL,
+                            &rctx->default_shell);
+    if (ret != EOK) goto fail;
+
     /* Read session_recording section */
     ret = session_recording_conf_load(rctx, rctx->cdb, &rctx->sr_conf);
     if (ret != EOK) {
diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h
index acb3c4a..f4a1130 100644
--- a/src/responder/nss/nss_private.h
+++ b/src/responder/nss/nss_private.h
@@ -74,12 +74,6 @@ struct nss_ctx {
     char *override_homedir;
     char *fallback_homedir;
     char *homedir_substr;
-    char **allowed_shells;
-    char *override_shell;
-    char **vetoed_shells;
-    char **etc_shells;
-    char *shell_fallback;
-    char *default_shell;
     const char **extra_attributes;
 
     /* Enumeration. */
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
index 7ea987c..e4c2f9f 100644
--- a/src/responder/nss/nss_protocol_pwent.c
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -121,7 +121,7 @@ nss_get_homedir(TALLOC_CTX *mem_ctx,
 
 static const char *
 nss_get_shell_override(struct ldb_message *msg,
-                       struct nss_ctx *nss_ctx,
+                       struct resp_ctx *rctx,
                        struct sss_domain_info *domain)
 {
     const char *shell;
@@ -131,8 +131,8 @@ nss_get_shell_override(struct ldb_message *msg,
      * the server for the login shell. */
     if (domain->override_shell) {
         return domain->override_shell;
-    } else if (nss_ctx->override_shell) {
-        return nss_ctx->override_shell;
+    } else if (rctx->override_shell) {
+        return rctx->override_shell;
     }
 
     shell = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_SHELL,
@@ -141,56 +141,56 @@ nss_get_shell_override(struct ldb_message *msg,
         /* Check whether there is a default shell specified */
         if (domain->default_shell) {
             return domain->default_shell;
-        } else if (nss_ctx->default_shell) {
-            return nss_ctx->default_shell;
+        } else if (rctx->default_shell) {
+            return rctx->default_shell;
         }
 
         return "";
     }
 
-    if (nss_ctx->allowed_shells == NULL && nss_ctx->vetoed_shells == NULL) {
+    if (rctx->allowed_shells == NULL && rctx->vetoed_shells == NULL) {
         return shell;
     }
 
-    if (nss_ctx->vetoed_shells) {
-        for (i = 0; nss_ctx->vetoed_shells[i]; i++) {
-            if (strcmp(nss_ctx->vetoed_shells[i], shell) == 0) {
+    if (rctx->vetoed_shells) {
+        for (i = 0; rctx->vetoed_shells[i]; i++) {
+            if (strcmp(rctx->vetoed_shells[i], shell) == 0) {
                 DEBUG(SSSDBG_FUNC_DATA,
                       "The shell '%s' is vetoed. Using fallback.\n",
                       shell);
-                return nss_ctx->shell_fallback;
+                return rctx->shell_fallback;
             }
         }
     }
 
-    if (nss_ctx->etc_shells) {
-        for (i = 0; nss_ctx->etc_shells[i]; i++) {
-            if (strcmp(shell, nss_ctx->etc_shells[i]) == 0) {
+    if (rctx->etc_shells) {
+        for (i = 0; rctx->etc_shells[i]; i++) {
+            if (strcmp(shell, rctx->etc_shells[i]) == 0) {
                 DEBUG(SSSDBG_TRACE_ALL,
                       "Shell %s found in /etc/shells\n", shell);
                 break;
             }
         }
 
-        if (nss_ctx->etc_shells[i]) {
+        if (rctx->etc_shells[i]) {
             DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", shell);
             return shell;
         }
     }
 
-    if (nss_ctx->allowed_shells) {
-        if (strcmp(nss_ctx->allowed_shells[0], "*") == 0) {
+    if (rctx->allowed_shells) {
+        if (strcmp(rctx->allowed_shells[0], "*") == 0) {
             DEBUG(SSSDBG_FUNC_DATA,
                   "The shell '%s' is allowed but does not exist. "
                   "Using fallback\n", shell);
-            return nss_ctx->shell_fallback;
+            return rctx->shell_fallback;
         } else {
-            for (i = 0; nss_ctx->allowed_shells[i]; i++) {
-                if (strcmp(nss_ctx->allowed_shells[i], shell) == 0) {
+            for (i = 0; rctx->allowed_shells[i]; i++) {
+                if (strcmp(rctx->allowed_shells[i], shell) == 0) {
                     DEBUG(SSSDBG_FUNC_DATA,
                           "The shell '%s' is allowed but does not exist. "
                           "Using fallback\n", shell);
-                    return nss_ctx->shell_fallback;
+                    return rctx->shell_fallback;
                 }
             }
         }
@@ -263,7 +263,7 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
         }
     }
     if (shell == NULL) {
-        shell = nss_get_shell_override(msg, nss_ctx, domain);
+        shell = nss_get_shell_override(msg, nss_ctx->rctx, domain);
     }
 
     /* Convert to sized strings. */
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index 807b5e8..3135444 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -52,9 +52,6 @@
 #define DEFAULT_PWFIELD "*"
 #define DEFAULT_NSS_FD_LIMIT 8192
 
-#define SHELL_REALLOC_INCREMENT 5
-#define SHELL_REALLOC_MAX       50
-
 static int nss_clear_memcache(struct sbus_request *dbus_req, void *data);
 static int nss_clear_netgroup_hash_table(struct sbus_request *dbus_req, void *data);
 
@@ -150,72 +147,6 @@ static int nss_clear_netgroup_hash_table(struct sbus_request *dbus_req, void *da
     return sbus_request_return_and_finish(dbus_req, DBUS_TYPE_INVALID);
 }
 
-static errno_t nss_get_etc_shells(TALLOC_CTX *mem_ctx, char ***_shells)
-{
-    int i = 0;
-    char *sh;
-    char **shells = NULL;
-    TALLOC_CTX *tmp_ctx;
-    errno_t ret;
-    int size;
-
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) return ENOMEM;
-
-    shells = talloc_array(tmp_ctx, char *, SHELL_REALLOC_INCREMENT);
-    if (!shells) {
-        ret = ENOMEM;
-        goto done;
-    }
-    size = SHELL_REALLOC_INCREMENT;
-
-    setusershell();
-    while ((sh = getusershell())) {
-        shells[i] = talloc_strdup(shells, sh);
-        if (!shells[i]) {
-            endusershell();
-            ret = ENOMEM;
-            goto done;
-        }
-        DEBUG(SSSDBG_TRACE_FUNC, "Found shell %s in /etc/shells\n", shells[i]);
-        i++;
-
-        if (i == size) {
-            size += SHELL_REALLOC_INCREMENT;
-            if (size > SHELL_REALLOC_MAX) {
-                DEBUG(SSSDBG_FATAL_FAILURE,
-                      "Reached maximum number of shells [%d]. "
-                          "Users may be denied access. "
-                          "Please check /etc/shells for sanity\n",
-                          SHELL_REALLOC_MAX);
-                break;
-            }
-            shells = talloc_realloc(NULL, shells, char *,
-                                    size);
-            if (!shells) {
-                ret = ENOMEM;
-                goto done;
-            }
-        }
-    }
-    endusershell();
-
-    if (i + 1 < size) {
-        shells = talloc_realloc(NULL, shells, char *, i + 1);
-        if (!shells) {
-            ret = ENOMEM;
-            goto done;
-        }
-    }
-    shells[i] = NULL;
-
-    *_shells = talloc_move(mem_ctx, &shells);
-    ret = EOK;
-done:
-    talloc_zfree(tmp_ctx);
-    return ret;
-}
-
 static int nss_get_config(struct nss_ctx *nctx,
                           struct confdb_ctx *cdb)
 {
@@ -265,36 +196,6 @@ static int nss_get_config(struct nss_ctx *nctx,
     if (ret != EOK) goto done;
 
     ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
-                            CONFDB_NSS_OVERRIDE_SHELL, NULL,
-                            &nctx->override_shell);
-    if (ret != EOK && ret != ENOENT) goto done;
-
-    ret = confdb_get_string_as_list(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
-                                    CONFDB_NSS_ALLOWED_SHELL,
-                                    &nctx->allowed_shells);
-    if (ret != EOK && ret != ENOENT) goto done;
-
-    ret = confdb_get_string_as_list(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
-                                    CONFDB_NSS_VETOED_SHELL,
-                                    &nctx->vetoed_shells);
-    if (ret != EOK && ret != ENOENT) goto done;
-
-    ret = nss_get_etc_shells(nctx, &nctx->etc_shells);
-    if (ret != EOK) goto done;
-
-    ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
-                            CONFDB_NSS_SHELL_FALLBACK,
-                            CONFDB_DEFAULT_SHELL_FALLBACK,
-                            &nctx->shell_fallback);
-    if (ret != EOK) goto done;
-
-    ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
-                            CONFDB_NSS_DEFAULT_SHELL,
-                            NULL,
-                            &nctx->default_shell);
-    if (ret != EOK) goto done;
-
-    ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
                             CONFDB_NSS_HOMEDIR_SUBSTRING,
                             CONFDB_DEFAULT_HOMEDIR_SUBSTRING,
                             &nctx->homedir_substr);

From 822ef2fa57e3644c19604f509dbb2d43d038988c Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Wed, 29 Mar 2017 16:25:19 +0300
Subject: [PATCH 16/17] NSS: Move nss_get_shell_override to responder utils

Move nss_get_shell_override to common responder utils and rename it to
sss_resp_get_shell_override to make it available to other responders. In
particular let PAM responder use it to provide original shell when it is
overriden for session recording.
---
 src/responder/common/responder.h       |  5 ++
 src/responder/common/responder_utils.c | 83 +++++++++++++++++++++++++++++++++
 src/responder/nss/nss_protocol_pwent.c | 85 +---------------------------------
 3 files changed, 89 insertions(+), 84 deletions(-)

diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 12d4e71..0277071 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -403,4 +403,9 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
 
 errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx);
 
+const char *
+sss_resp_get_shell_override(struct ldb_message *msg,
+                            struct resp_ctx *rctx,
+                            struct sss_domain_info *domain);
+
 #endif /* __SSS_RESPONDER_H__ */
diff --git a/src/responder/common/responder_utils.c b/src/responder/common/responder_utils.c
index b02212d..e1b042d 100644
--- a/src/responder/common/responder_utils.c
+++ b/src/responder/common/responder_utils.c
@@ -193,3 +193,86 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
     talloc_free(tmp_ctx);
     return name;
 }
+
+const char *
+sss_resp_get_shell_override(struct ldb_message *msg,
+                            struct resp_ctx *rctx,
+                            struct sss_domain_info *domain)
+{
+    const char *shell;
+    int i;
+
+    /* Check whether we are unconditionally overriding
+     * the server for the login shell. */
+    if (domain->override_shell) {
+        return domain->override_shell;
+    } else if (rctx->override_shell) {
+        return rctx->override_shell;
+    }
+
+    shell = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_SHELL,
+                                                 NULL);
+    if (shell == NULL) {
+        /* Check whether there is a default shell specified */
+        if (domain->default_shell) {
+            return domain->default_shell;
+        } else if (rctx->default_shell) {
+            return rctx->default_shell;
+        }
+
+        return "";
+    }
+
+    if (rctx->allowed_shells == NULL && rctx->vetoed_shells == NULL) {
+        return shell;
+    }
+
+    if (rctx->vetoed_shells) {
+        for (i = 0; rctx->vetoed_shells[i]; i++) {
+            if (strcmp(rctx->vetoed_shells[i], shell) == 0) {
+                DEBUG(SSSDBG_FUNC_DATA,
+                      "The shell '%s' is vetoed. Using fallback.\n",
+                      shell);
+                return rctx->shell_fallback;
+            }
+        }
+    }
+
+    if (rctx->etc_shells) {
+        for (i = 0; rctx->etc_shells[i]; i++) {
+            if (strcmp(shell, rctx->etc_shells[i]) == 0) {
+                DEBUG(SSSDBG_TRACE_ALL,
+                      "Shell %s found in /etc/shells\n", shell);
+                break;
+            }
+        }
+
+        if (rctx->etc_shells[i]) {
+            DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", shell);
+            return shell;
+        }
+    }
+
+    if (rctx->allowed_shells) {
+        if (strcmp(rctx->allowed_shells[0], "*") == 0) {
+            DEBUG(SSSDBG_FUNC_DATA,
+                  "The shell '%s' is allowed but does not exist. "
+                  "Using fallback\n", shell);
+            return rctx->shell_fallback;
+        } else {
+            for (i = 0; rctx->allowed_shells[i]; i++) {
+                if (strcmp(rctx->allowed_shells[i], shell) == 0) {
+                    DEBUG(SSSDBG_FUNC_DATA,
+                          "The shell '%s' is allowed but does not exist. "
+                          "Using fallback\n", shell);
+                    return rctx->shell_fallback;
+                }
+            }
+        }
+    }
+
+    DEBUG(SSSDBG_FUNC_DATA,
+          "The shell '%s' is not allowed and does not exist.\n", shell);
+
+    return NOLOGIN_SHELL;
+}
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
index e4c2f9f..387e37c 100644
--- a/src/responder/nss/nss_protocol_pwent.c
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -119,89 +119,6 @@ nss_get_homedir(TALLOC_CTX *mem_ctx,
     return homedir;
 }
 
-static const char *
-nss_get_shell_override(struct ldb_message *msg,
-                       struct resp_ctx *rctx,
-                       struct sss_domain_info *domain)
-{
-    const char *shell;
-    int i;
-
-    /* Check whether we are unconditionally overriding
-     * the server for the login shell. */
-    if (domain->override_shell) {
-        return domain->override_shell;
-    } else if (rctx->override_shell) {
-        return rctx->override_shell;
-    }
-
-    shell = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_SHELL,
-                                                 NULL);
-    if (shell == NULL) {
-        /* Check whether there is a default shell specified */
-        if (domain->default_shell) {
-            return domain->default_shell;
-        } else if (rctx->default_shell) {
-            return rctx->default_shell;
-        }
-
-        return "";
-    }
-
-    if (rctx->allowed_shells == NULL && rctx->vetoed_shells == NULL) {
-        return shell;
-    }
-
-    if (rctx->vetoed_shells) {
-        for (i = 0; rctx->vetoed_shells[i]; i++) {
-            if (strcmp(rctx->vetoed_shells[i], shell) == 0) {
-                DEBUG(SSSDBG_FUNC_DATA,
-                      "The shell '%s' is vetoed. Using fallback.\n",
-                      shell);
-                return rctx->shell_fallback;
-            }
-        }
-    }
-
-    if (rctx->etc_shells) {
-        for (i = 0; rctx->etc_shells[i]; i++) {
-            if (strcmp(shell, rctx->etc_shells[i]) == 0) {
-                DEBUG(SSSDBG_TRACE_ALL,
-                      "Shell %s found in /etc/shells\n", shell);
-                break;
-            }
-        }
-
-        if (rctx->etc_shells[i]) {
-            DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", shell);
-            return shell;
-        }
-    }
-
-    if (rctx->allowed_shells) {
-        if (strcmp(rctx->allowed_shells[0], "*") == 0) {
-            DEBUG(SSSDBG_FUNC_DATA,
-                  "The shell '%s' is allowed but does not exist. "
-                  "Using fallback\n", shell);
-            return rctx->shell_fallback;
-        } else {
-            for (i = 0; rctx->allowed_shells[i]; i++) {
-                if (strcmp(rctx->allowed_shells[i], shell) == 0) {
-                    DEBUG(SSSDBG_FUNC_DATA,
-                          "The shell '%s' is allowed but does not exist. "
-                          "Using fallback\n", shell);
-                    return rctx->shell_fallback;
-                }
-            }
-        }
-    }
-
-    DEBUG(SSSDBG_FUNC_DATA,
-          "The shell '%s' is not allowed and does not exist.\n", shell);
-
-    return NOLOGIN_SHELL;
-}
-
 static errno_t
 nss_get_pwent(TALLOC_CTX *mem_ctx,
               struct nss_ctx *nss_ctx,
@@ -263,7 +180,7 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
         }
     }
     if (shell == NULL) {
-        shell = nss_get_shell_override(msg, nss_ctx->rctx, domain);
+        shell = sss_resp_get_shell_override(msg, nss_ctx->rctx, domain);
     }
 
     /* Convert to sized strings. */

From 50e83b7e6e667e338e76e4259b3608390c273f0b Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Wed, 29 Mar 2017 16:47:41 +0300
Subject: [PATCH 17/17] PAM: Export original shell to tlog-rec

Add exporting of original user shell (as returned by NSS) as an
environment variable for use by tlog-rec, when session recording is
enabled for the user. This lets tlog-rec start the actual user shell,
after tlog-rec is started in its place.
---
 src/responder/pam/pamsrv_cmd.c | 93 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index ba2563c..7962047 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -682,6 +682,90 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd);
 static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
                                     time_t expire_date, time_t delayed_until, bool cached_auth);
 
+/*
+ * Add a request to add a variable to the PAM user environment, containing the
+ * actual (not overridden) user shell.
+ */
+static int pam_reply_export_shell(struct pam_auth_req *preq,
+                                  const char *var_name)
+{
+    int ret;
+    TALLOC_CTX *ctx = NULL;
+    bool enabled;
+    const char *enabled_str;
+    const char *shell;
+    char *buf;
+
+    /* Create temporary talloc context */
+    ctx = talloc_new(NULL);
+    if (ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Check if session recording is enabled */
+    if (preq->cctx->rctx->sr_conf.scope ==
+            SESSION_RECORDING_CONF_SCOPE_NONE) {
+        enabled = false;
+    } else if (preq->cctx->rctx->sr_conf.scope ==
+            SESSION_RECORDING_CONF_SCOPE_ALL) {
+        enabled = true;
+    } else {
+        enabled_str = ldb_msg_find_attr_as_string(preq->user_obj,
+                                                  SYSDB_SESSION_RECORDING, NULL);
+        if (enabled_str == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "%s attribute not found\n", SYSDB_SESSION_RECORDING);
+            ret = ENOENT;
+            goto done;
+        } else if (strcmp(enabled_str, "TRUE") == 0) {
+            enabled = true;
+        } else if (strcmp(enabled_str, "FALSE") == 0) {
+            enabled = false;
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE, "invalid value of %s attribute: %s\n",
+                  SYSDB_SESSION_RECORDING, enabled_str);
+            ret = ENOENT;
+            goto done;
+        }
+    }
+
+    /* Export original shell if recording is enabled and so it's overridden */
+    if (enabled) {
+        /* Extract the shell */
+        shell = sss_resp_get_shell_override(preq->user_obj,
+                                            preq->cctx->rctx, preq->domain);
+        if (shell == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "user has no shell\n");
+            ret = ENOENT;
+            goto done;
+        }
+
+        /* Format environment entry */
+        buf = talloc_asprintf(ctx, "%s=%s", var_name, shell);
+        if (buf == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        /* Add request to add the entry to user environment */
+        ret = pam_add_response(preq->pd, SSS_PAM_ENV_ITEM,
+                               strlen(buf) + 1, (uint8_t *)buf);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(ctx);
+    return ret;
+}
+
 static void pam_reply(struct pam_auth_req *preq)
 {
     struct cli_ctx *cctx;
@@ -918,6 +1002,15 @@ static void pam_reply(struct pam_auth_req *preq)
         }
     }
 
+    /* Export non-overridden shell to tlog-rec when opening the session */
+    if (pd->cmd == SSS_PAM_OPEN_SESSION && pd->pam_status == PAM_SUCCESS) {
+        ret = pam_reply_export_shell(preq, "TLOG_REC_SHELL");
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "failed to export the shell to tlog-rec.\n");
+            goto done;
+        }
+    }
+
     resp_c = 0;
     resp_size = 0;
     resp = pd->resp_list;
_______________________________________________
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