From dc0ffbc8f637adbf6432132aece38cd39ed733c0 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <Nikolai.Kondrashov@redhat.com>
Date: Fri, 15 Jul 2016 13:16:39 +0300
Subject: [PATCH 12/15] NSS: Add session recording shell substitution

Add support for substituting user shell with a session recording shell
for users and groups with session recording enabled, for getpwnam,
getpwuid, and getpwent calls.
---
 Makefile.am                              |   6 +-
 src/responder/common/session_recording.c | 305 +++++++++++++++++++++++++++++++
 src/responder/common/session_recording.h | 143 +++++++++++++++
 src/responder/nss/nsssrv.c               |  22 +++
 src/responder/nss/nsssrv.h               |   9 +
 src/responder/nss/nsssrv_cmd.c           | 203 +++++++++++++++++++-
 src/responder/nss/nsssrv_private.h       |  18 ++
 7 files changed, 695 insertions(+), 11 deletions(-)
 create mode 100644 src/responder/common/session_recording.c
 create mode 100644 src/responder/common/session_recording.h

diff --git a/Makefile.am b/Makefile.am
index 8b9240f..99ffbc4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -490,6 +490,7 @@ SSSD_RESPONDER_OBJ = \
     src/responder/common/responder_get_domains.c \
     src/responder/common/responder_utils.c \
     src/responder/common/responder_cache_req.c \
+    src/responder/common/session_recording.c \
     src/responder/common/data_provider/rdp_message.c \
     src/responder/common/data_provider/rdp_client.c \
     src/monitor/monitor_iface_generated.c \
@@ -602,6 +603,7 @@ dist_noinst_HEADERS = \
     src/responder/common/responder_packet.h \
     src/responder/common/responder_sbus.h \
     src/responder/common/responder_cache_req.h \
+    src/responder/common/session_recording.h \
     src/responder/common/data_provider/rdp.h \
     src/responder/pam/pamsrv.h \
     src/responder/pam/pam_helpers.h \
@@ -2091,6 +2093,7 @@ TEST_MOCK_RESP_OBJ = \
      src/responder/common/negcache_files.c \
      src/responder/common/negcache.c \
      src/responder/common/responder_common.c \
+     src/responder/common/session_recording.c \
      src/responder/common/data_provider/rdp_message.c \
      src/responder/common/data_provider/rdp_client.c \
      src/responder/common/responder_utils.c \
@@ -2117,7 +2120,8 @@ nss_srv_tests_SOURCES = \
      src/responder/nss/nsssrv_cmd.c \
      src/responder/nss/nsssrv_netgroup.c \
      src/responder/nss/nsssrv_services.c \
-     src/responder/nss/nsssrv_mmap_cache.c
+     src/responder/nss/nsssrv_mmap_cache.c \
+     $(NULL)
 nss_srv_tests_CFLAGS = \
     $(AM_CFLAGS)
 nss_srv_tests_LDFLAGS = \
diff --git a/src/responder/common/session_recording.c b/src/responder/common/session_recording.c
new file mode 100644
index 0000000..618791f
--- /dev/null
+++ b/src/responder/common/session_recording.c
@@ -0,0 +1,305 @@
+/*
+    SSSD
+
+    Responder session recording functions
+
+    Authors:
+        Nikolai Kondrashov <Nikolai.Kondrashov@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 "responder/common/session_recording.h"
+#include "responder/common/responder_cache_req.h"
+
+int 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_SCOPE_NONE;
+    } else if (strcasecmp(str, "some") == 0) {
+        pconf->scope = SESSION_RECORDING_SCOPE_SOME;
+    } else if (strcasecmp(str, "all") == 0) {
+        pconf->scope = SESSION_RECORDING_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;
+}
+
+/**
+ * Internal state of the asynchronous request to retrieve information on
+ * groups for which session recording is enabled.
+ */
+struct session_recording_get_groups_state {
+    TALLOC_CTX *mem_ctx;            /**< Memory context to allocate results
+                                         with */
+    struct resp_ctx *rctx;          /**< Responder context to work within */
+    char **pname;                   /**< Pointer to name of the next group to
+                                         retrieve info about */
+    struct ldb_result **presult;    /**< Next location to store retrieved
+                                         result pointer in */
+};
+
+/**
+ * Handle a result of a group info request and submit another one, if there's
+ * any.
+ *
+ * @param subreq    The completed group info request.
+ */
+static void session_recording_get_groups_step(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct session_recording_get_groups_state *state =
+            tevent_req_data(req, struct session_recording_get_groups_state);
+    struct resp_ctx *rctx = state->rctx;
+
+    /* Get the lookup result, move onto next array element if we got one */
+    ret = cache_req_group_by_name_recv(state->mem_ctx, subreq,
+                                       state->presult, NULL, NULL);
+    talloc_zfree(subreq);
+    if (ret == EOK) {
+        state->presult++;
+    } else if (ret != ENOENT) {
+        goto done;
+    }
+
+    /* Move onto the next group name */
+    state->pname++;
+
+    /* If there are no more names */
+    if (*state->pname == NULL) {
+        tevent_req_done(req);
+        ret = EOK;
+        goto done;
+    }
+
+    /* Lookup next name */
+    /* TODO: Figure out proper caching parameters */
+    subreq = cache_req_group_by_name_send(state->mem_ctx, rctx->ev, rctx,
+                                          rctx->ncache, 0, NULL,
+                                          *state->pname);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq,
+                            session_recording_get_groups_step, req);
+    return;
+
+done:
+    tevent_req_error(req, ret);
+}
+
+struct tevent_req *session_recording_get_groups_send(
+                                        TALLOC_CTX *mem_ctx,
+                                        struct resp_ctx *rctx,
+                                        char **name_list,
+                                        struct ldb_result ***presult_list)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct session_recording_get_groups_state *state;
+    char **pname;
+    size_t num;
+
+    /* Create the tracking "request" */
+    req = tevent_req_create(mem_ctx, &state,
+                            struct session_recording_get_groups_state);
+    if (req == NULL) {
+         return NULL;
+    }
+
+    /* Count groups to request */
+    num = 0;
+    pname = name_list;
+    if (pname != NULL) {
+        for (; *pname != NULL; pname++, num++);
+    }
+
+    /* Allocate array for lookup results, include terminating NULL */
+    *presult_list = talloc_zero_array(mem_ctx, struct ldb_result *, num + 1);
+    if (*presult_list == NULL) {
+        ret = ENOMEM;
+        goto error;
+    }
+
+    /* TODO Handle zero groups */
+
+    /* Fill state */
+    state->mem_ctx = mem_ctx;
+    state->rctx = rctx;
+    state->pname = name_list;
+    state->presult = *presult_list;
+
+    /* Start first group lookup */
+    subreq = cache_req_group_by_name_send(mem_ctx, rctx->ev, rctx,
+                                          rctx->ncache, 0, NULL,
+                                          *state->pname);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto error;
+    }
+    tevent_req_set_callback(subreq,
+                            session_recording_get_groups_step, req);
+
+    return req;
+
+error:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, rctx->ev);
+    return req;
+}
+
+errno_t session_recording_get_groups_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+errno_t session_recording_is_enabled(enum session_recording_scope scope,
+                                     char **users,
+                                     struct ldb_result **group_results,
+                                     struct sss_domain_info *dom,
+                                     const char *orig_name,
+                                     const char *name,
+                                     bool *penabled)
+{
+    errno_t ret;
+    bool enabled;
+    char **pname;
+    struct ldb_result **pgroup_result;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    if (dom == NULL || orig_name == NULL ||
+        name == NULL || penabled == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    switch (scope) {
+        case SESSION_RECORDING_SCOPE_NONE:
+            enabled = false;
+            ret = EOK;
+            goto done;
+        case SESSION_RECORDING_SCOPE_ALL:
+            enabled = true;
+            ret = EOK;
+            goto done;
+        case SESSION_RECORDING_SCOPE_SOME:
+            /*
+             * Check if user name matches recorded names
+             */
+            if (users != NULL) {
+                for (pname = users; *pname != NULL; pname++) {
+                    if (strcmp(name, *pname) == 0) {
+                        enabled = true;
+                        ret = EOK;
+                        goto done;
+                    }
+                }
+            }
+
+            /*
+             * Check if user is in a group with session recording enabled
+             */
+            pgroup_result = group_results;
+            if (pgroup_result != NULL && *pgroup_result != NULL) {
+                struct ldb_result *user_result;
+                struct ldb_result *group_result;
+                size_t i;
+                size_t j;
+
+                /* Retrieve groups the user is member of */
+                ret = sysdb_initgroups_with_views(tmp_ctx, dom, orig_name, &user_result);
+                if (ret != EOK) {
+                    goto done;
+                }
+                /*
+                 * For each result of a session recording-enabled group lookup
+                 */
+                do {
+                    group_result = *pgroup_result;
+                    /* For each message (match) in the result */
+                    for (i = 0; i < group_result->count; i++) {
+                        /* For each user's group */
+                        for (j = 1; j < user_result->count; j++) {
+                            /* If their DN's match */
+                            if (ldb_dn_compare(
+                                        group_result->msgs[i]->dn,
+                                        user_result->msgs[j]->dn) == 0) {
+                                enabled = true;
+                                ret = EOK;
+                                goto done;
+                            }
+                        }
+                    }
+                } while (*++pgroup_result != NULL);
+            }
+            enabled = false;
+            ret = EOK;
+            break;
+        default:
+            ret = EINVAL;
+            goto done;
+    }
+
+done:
+    talloc_zfree(tmp_ctx);
+    if (ret == EOK) {
+        *penabled = enabled;
+    }
+    return ret;
+}
diff --git a/src/responder/common/session_recording.h b/src/responder/common/session_recording.h
new file mode 100644
index 0000000..b2a8cc7
--- /dev/null
+++ b/src/responder/common/session_recording.h
@@ -0,0 +1,143 @@
+/*
+    SSSD
+
+    Responder session recording functions
+
+    Authors:
+        Nikolai Kondrashov <Nikolai.Kondrashov@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 __SSS_RESPONDER_SESSION_RECORDING_H__
+#define __SSS_RESPONDER_SESSION_RECORDING_H__
+
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+
+/** Scope of users whose session should be recorded */
+enum session_recording_scope {
+    SESSION_RECORDING_SCOPE_NONE,    /**< None, no users */
+    SESSION_RECORDING_SCOPE_SOME,    /**< Some users specified elsewhere */
+    SESSION_RECORDING_SCOPE_ALL      /**< All users */
+};
+
+/** Session recording configuration (from "session_recording" section) */
+struct session_recording_conf {
+    enum session_recording_scope    scope;  /**< Session recording scope:
+                                                 whether to record nobody,
+                                                 everyone, or some
+                                                 users/groups */
+    char                          **users;  /**< NULL-terminated list of users
+                                                 whose session should be
+                                                 recorded. Can be NULL,
+                                                 meaning empty list. Only
+                                                 applicable if scope is "some"
+                                                 */
+    char                          **groups; /**< 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"
+                                                 */
+};
+
+/**
+ * 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 configuration.
+ *
+ * @return Status code:
+ *          ENOMEM - memory allocation failed,
+ *          EINVAL - configuration was invalid,
+ *          EIO - an I/O error occurred while communicating with the ConfDB.
+ */
+extern int session_recording_conf_load(TALLOC_CTX *mem_ctx,
+                                       struct confdb_ctx *cdb, 
+                                       struct session_recording_conf *pconf);
+
+/**
+ * Start an asynchronous request to retrieve groups for which session
+ * recording is enabled, for an abstract responder context.
+ *
+ * @param mem_ctx       The memory context to allocate results and request
+ *                      state with.
+ * @param rctx          Abstract responder context to operate within.
+ * @param name_list     NULL-terminated list of group names to retrieve info
+ *                      for. Can be NULL. Must be valid during the whole
+ *                      request lifetime.
+ * @param presult_list  Location for the pointer to a NULL-terminated array of
+ *                      pointers to retrieved group results, allocated with
+ *                      mem_ctx.
+ *
+ * @return The created asynchronous request, or NULL, if failed to allocate
+ *         memory for it.
+ */
+extern struct tevent_req *session_recording_get_groups_send(
+                                        TALLOC_CTX *mem_ctx,
+                                        struct resp_ctx *rctx,
+                                        char **name_list,
+                                        struct ldb_result ***presult_list);
+
+/**
+ * Retrieve status code for a finished request to retrieve groups for which
+ * session recording is enabled.
+ *
+ * @return Status code.
+ */
+extern errno_t session_recording_get_groups_recv(struct tevent_req *req);
+
+/**
+ * Check if session recording is enabled for a user, according to session
+ * recording configuration.
+ *
+ * @param scope         Session recording scope.
+ * @param users         A NULL-terminated list of names of users (as returned
+ *                      to NSS client) with session recording enabled. Only
+ *                      valid if scope is "some". Can be NULL.
+ * @param group_results A NULL-terminated list of ldb_results for groups with
+ *                      session recording enabled. Only valid if scope is
+ *                      "some". Can be NULL.
+ * @param dom           Domain the user belongs to. Used to retrieve the
+ *                      groups the user belongs to.
+ * @param orig_name     User's name. Used to retrieve the groups the user
+ *                      belongs to.
+ * @param name          User's name as returned to NSS client. Used to match
+ *                      against the list of users with enabled session
+ *                      recording.
+ * @param penabled      Location for the "enabled" flag: true if session
+ *                      recording is enabled, false otherwise. Not changed in
+ *                      case of error.
+ *
+ * @return Status code:
+ *          EOK     - check completed successfully,
+ *          EINVAL  - invalid arguments supplied,
+ *          ENOMEM  - memory allocation failed,
+ *          other   - failed retrieving user info.
+ */
+extern int session_recording_is_enabled(enum session_recording_scope scope,
+                                        char **users,
+                                        struct ldb_result **group_results,
+                                        struct sss_domain_info *dom,
+                                        const char *orig_name,
+                                        const char *name,
+                                        bool *penabled);
+
+#endif /* __SSS_RESPONDER_SESSION_RECORDING_H__ */
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index 06d58f2..e32d1b3 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -323,6 +323,28 @@ static int nss_get_config(struct nss_ctx *nctx,
         }
     }
 
+    /* Read session_recording section */
+    ret = session_recording_conf_load(nctx, cdb,
+                                      &nctx->session_recording_conf);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed loading session recording configuration: %s\n",
+              strerror(ret));
+        goto done;
+    }
+
+    /* If configured to enable session recording for specific groups */
+    if (nctx->session_recording_conf.scope ==
+            SESSION_RECORDING_SCOPE_SOME &&
+        nctx->session_recording_conf.groups != NULL &&
+        nctx->session_recording_conf.groups[0] != NULL) {
+        /* Always request user's groups when requesting user info */
+        nctx->user_req_type = SSS_DP_INITGROUPS;
+    } else {
+        /* Request normal user info only */
+        nctx->user_req_type = SSS_DP_USER;
+    }
+
     ret = 0;
 done:
     return ret;
diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
index d4a80f7..6ac057c 100644
--- a/src/responder/nss/nsssrv.h
+++ b/src/responder/nss/nsssrv.h
@@ -34,6 +34,7 @@
 #include "sbus/sssd_dbus.h"
 #include "responder/common/responder_packet.h"
 #include "responder/common/responder.h"
+#include "responder/common/session_recording.h"
 #include "lib/idmap/sss_idmap.h"
 
 #define NSS_PACKET_MAX_RECV_SIZE 1024
@@ -75,6 +76,14 @@ struct nss_ctx {
     struct sss_names_ctx *global_names;
 
     const char **extra_attributes;
+
+    struct session_recording_conf
+                session_recording_conf;     /**< Session recording
+                                                 configuration */
+    enum sss_dp_acct_type user_req_type;    /**< Request type to use when
+                                                 refreshing cached user
+                                                 information from the
+                                                 provider */
 };
 
 struct nss_packet;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 9eccf78..f14a303 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -29,6 +29,7 @@
 #include "responder/nss/nsssrv_netgroup.h"
 #include "responder/nss/nsssrv_services.h"
 #include "responder/nss/nsssrv_mmap_cache.h"
+#include "responder/common/session_recording.h"
 #include "responder/common/negcache.h"
 #include "responder/common/responder_cache_req.h"
 #include "providers/data_provider.h"
@@ -394,6 +395,7 @@ static int fill_pwent(struct sss_packet *packet,
     int i, ret, num;
     const char *domain = dom->name;
     bool packet_initialized = false;
+    bool session_recording_enabled;
     int ncret;
     TALLOC_CTX *tmp_ctx = NULL;
     struct sss_nss_homedir_ctx homedir_ctx;
@@ -491,12 +493,27 @@ static int fill_pwent(struct sss_packet *packet,
             to_sized_string(&homedir, tmpstr);
         }
 
-        tmpstr = get_shell_override(tmp_ctx, msg, nctx, dom);
-        if (!tmpstr) {
-            to_sized_string(&shell, "");
+        ret = session_recording_is_enabled(nctx->session_recording_conf.scope,
+                                           nctx->session_recording_conf.users,
+                                           cmdctx->session_recording_groups,
+                                           dom, orig_name, name->str,
+                                           &session_recording_enabled);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Failed checking if session recording is enabled for user "
+                  "[%s] in [%s]: %s! Skipping user.\n",
+                  name->str, domain, strerror(ret));
+            continue;
+        }
+        if (session_recording_enabled) {
+            tmpstr = SESSION_RECORDING_SHELL;
         } else {
-            to_sized_string(&shell, tmpstr);
+            tmpstr = get_shell_override(tmp_ctx, msg, nctx, dom);
+        }
+        if (!tmpstr) {
+            tmpstr = "";
         }
+        to_sized_string(&shell, tmpstr);
 
         rsize = 2 * sizeof(uint32_t) + name->len + gecos.len +
                                        homedir.len + shell.len + pwfield.len;
@@ -1162,7 +1179,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
                 extra_flag = NULL;
             }
 
-            ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, name, 0,
+            ret = check_cache(dctx, nctx, dctx->res, nctx->user_req_type,
+                              name, 0,
                               extra_flag, nss_cmd_getby_dp_callback, dctx);
             if (ret != EOK) {
                 /* Anything but EOK means we should reenter the mainloop
@@ -1480,6 +1498,9 @@ static int nss_check_name_of_well_known_sid(struct nss_cmd_ctx *cmdctx,
 
 static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx);
 static void nss_cmd_getbynam_get_domains_done(struct tevent_req *req);
+static int nss_cmd_getbynam_session_recording_get_groups(struct nss_dom_ctx *dctx);
+static void nss_cmd_getbynam_session_recording_get_groups_done(struct tevent_req *req);
+
 static int nss_cmd_getpwnam(struct cli_ctx *cctx)
 {
     return nss_cmd_getbynam(SSS_NSS_GETPWNAM, cctx);
@@ -1677,7 +1698,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
         }
     }
 
-    ret = nss_cmd_getbynam_search_and_reply(dctx);
+    ret = nss_cmd_getbynam_session_recording_get_groups(dctx);
 
 done:
     return nss_cmd_done(cmdctx, ret);
@@ -1730,6 +1751,57 @@ static void nss_cmd_getbynam_get_domains_done(struct tevent_req *req)
         /* Not fatal */
     }
 
+    ret = nss_cmd_getbynam_session_recording_get_groups(dctx);
+
+done:
+    nss_cmd_done(cmdctx, ret);
+}
+
+static int nss_cmd_getbynam_session_recording_get_groups(struct nss_dom_ctx *dctx)
+{
+    struct nss_ctx *nctx;
+    struct tevent_req *req;
+
+    nctx = talloc_get_type(dctx->cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
+
+    /* If we need to match a user against session recording groups */
+    if (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM &&
+        nctx->session_recording_conf.scope ==
+            SESSION_RECORDING_SCOPE_SOME &&
+        nctx->session_recording_conf.groups != NULL &&
+        nctx->session_recording_conf.groups[0] != NULL) {
+        /*
+         * Do an async request for groups with session recording enabled.
+         * Store them in the command context.
+         */
+        req = session_recording_get_groups_send(
+                                    dctx->cmdctx,
+                                    dctx->cmdctx->cctx->rctx,
+                                    nctx->session_recording_conf.groups,
+                                    &dctx->cmdctx->session_recording_groups);
+        if (req == NULL) {
+            return ENOMEM;
+        } else {
+            tevent_req_set_callback(req, nss_cmd_getbynam_session_recording_get_groups_done, dctx);
+            return EAGAIN;
+        }
+    }
+
+    return nss_cmd_getbynam_search_and_reply(dctx);
+}
+
+static void nss_cmd_getbynam_session_recording_get_groups_done(struct tevent_req *req)
+{
+    struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
+    struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+    errno_t ret;
+
+    ret = session_recording_get_groups_recv(req);
+    talloc_free(req);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = nss_cmd_getbynam_search_and_reply(dctx);
 
 done:
@@ -1836,7 +1908,7 @@ static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx)
                 extra_flag = NULL;
             }
 
-            ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, NULL,
+            ret = check_cache(dctx, nctx, dctx->res, nctx->user_req_type, NULL,
                               cmdctx->id, extra_flag, nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -1875,6 +1947,9 @@ done:
 static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx);
 static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx);
 static void nss_cmd_getbyid_get_domains_done(struct tevent_req *req);
+static int nss_cmd_getbyid_session_recording_get_groups(struct nss_dom_ctx *dctx);
+static void nss_cmd_getbyid_session_recording_get_groups_done(struct tevent_req *req);
+
 static int nss_cmd_getpwuid(struct cli_ctx *cctx)
 {
     return nss_cmd_getbyid(SSS_NSS_GETPWUID, cctx);
@@ -2047,7 +2122,7 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx)
         goto done;
     }
 
-    ret = nss_cmd_getbyid_search_and_reply(dctx);
+    ret = nss_cmd_getbyid_session_recording_get_groups(dctx);
 
 done:
     return nss_cmd_done(cmdctx, ret);
@@ -2071,6 +2146,59 @@ static void nss_cmd_getbyid_get_domains_done(struct tevent_req *req)
         /* Not fatal */
     }
 
+    ret = nss_cmd_getbyid_session_recording_get_groups(dctx);
+
+done:
+    nss_cmd_done(cmdctx, ret);
+}
+
+static int nss_cmd_getbyid_session_recording_get_groups(struct nss_dom_ctx *dctx)
+{
+    struct nss_ctx *nctx;
+    struct tevent_req *req;
+
+    nctx = talloc_get_type(dctx->cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
+
+    /* If we need to match a user against session recording groups */
+    if (dctx->cmdctx->cmd == SSS_NSS_GETPWUID &&
+        nctx->session_recording_conf.scope ==
+            SESSION_RECORDING_SCOPE_SOME &&
+        nctx->session_recording_conf.groups != NULL &&
+        nctx->session_recording_conf.groups[0] != NULL) {
+        /*
+         * Do an async request for groups with session recording enabled.
+         * Store them in the command context.
+         */
+        req = session_recording_get_groups_send(
+                                    dctx->cmdctx,
+                                    dctx->cmdctx->cctx->rctx,
+                                    nctx->session_recording_conf.groups,
+                                    &dctx->cmdctx->session_recording_groups);
+        if (req == NULL) {
+            return ENOMEM;
+        } else {
+            tevent_req_set_callback(req,
+                                    nss_cmd_getbyid_session_recording_get_groups_done,
+                                    dctx);
+            return EAGAIN;
+        }
+    }
+
+    return nss_cmd_getbyid_search_and_reply(dctx);
+}
+
+static void nss_cmd_getbyid_session_recording_get_groups_done(struct tevent_req *req)
+{
+    struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
+    struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
+    errno_t ret;
+
+    ret = session_recording_get_groups_recv(req);
+    talloc_free(req);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = nss_cmd_getbyid_search_and_reply(dctx);
 
 done:
@@ -2230,6 +2358,11 @@ struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
     step_ctx->rctx = client->rctx;
     step_ctx->cctx = client;
     step_ctx->returned_to_mainloop = false;
+    step_ctx->need_session_recording_groups =
+                    state->nctx->session_recording_conf.scope ==
+                        SESSION_RECORDING_SCOPE_SOME &&
+                    state->nctx->session_recording_conf.groups != NULL &&
+                    state->nctx->session_recording_conf.groups[0] != NULL;
 
     ret = nss_cmd_setpwent_step(step_ctx);
     if (ret != EOK && ret != EAGAIN) goto error;
@@ -2252,6 +2385,8 @@ static void setpwent_result_timeout(struct tevent_context *ev,
                                     struct tevent_timer *te,
                                     struct timeval current_time,
                                     void *pvt);
+static void nss_cmd_setpwent_session_recording_get_groups_done(
+                                                    struct tevent_req *req);
 
 /* nss_cmd_setpwent_step returns
  *   EOK if everything is done and the request needs to be posted explicitly
@@ -2271,6 +2406,23 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
     struct tevent_req *dpreq;
     struct dp_callback_ctx *cb_ctx;
 
+    if (step_ctx->need_session_recording_groups) {
+        struct tevent_req *req;
+        req = session_recording_get_groups_send(
+                                        pctx, rctx,
+                                        nctx->session_recording_conf.groups,
+                                        &pctx->session_recording_groups);
+        if (req == NULL) {
+            return ENOMEM;
+        } else {
+            tevent_req_set_callback(
+                        req,
+                        nss_cmd_setpwent_session_recording_get_groups_done,
+                        step_ctx);
+            return EAGAIN;
+        }
+    }
+
     while (dom) {
         while (dom && dom->enumerate == false) {
             dom = get_next_domain(dom, SSS_GND_DESCEND);
@@ -2304,7 +2456,7 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
             dctx->check_provider = false;
 
             dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
-                                          SSS_DP_USER, NULL, 0, NULL);
+                                            nctx->user_req_type, NULL, 0, NULL);
             if (!dpreq) {
                 DEBUG(SSSDBG_MINOR_FAILURE,
                       "Enum Cache refresh for domain [%s] failed."
@@ -2406,6 +2558,31 @@ static void setpwent_result_timeout(struct tevent_context *ev,
     talloc_zfree(nctx->pctx);
 }
 
+static void nss_cmd_setpwent_session_recording_get_groups_done(struct tevent_req *req)
+{
+    struct setent_step_ctx *step_ctx =
+                        tevent_req_callback_data(req, struct setent_step_ctx);
+    errno_t ret;
+
+    /* We're done, don't try again */
+    step_ctx->need_session_recording_groups = false;
+
+    ret = session_recording_get_groups_recv(req);
+    talloc_free(req);
+    if (ret != EOK) {
+        /* Notify any waiting processes of failure */
+        nss_setent_notify_error(step_ctx->nctx->pctx, ret);
+        return;
+    }
+
+    ret = nss_cmd_setpwent_step(step_ctx);
+    if (ret != EOK && ret != EAGAIN) {
+        /* Notify any waiting processes of failure */
+        nss_setent_notify_error(step_ctx->nctx->pctx, ret);
+        return;
+    }
+}
+
 static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
                                          const char *err_msg, void *ptr)
 {
@@ -2497,6 +2674,12 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx)
         return EOK;
     }
 
+    /*
+     * Use the groups setpwent retrieved as groups for which session recording
+     * is enabled.
+     */
+    cmdctx->session_recording_groups = nctx->pctx->session_recording_groups;
+
     return nss_cmd_getpwent_immediate(cmdctx);
 }
 
@@ -5268,7 +5451,7 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
     DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n",
               cmdctx->secid, dctx->domain->name);
 
-    ret = nss_cmd_getbyid_search_and_reply(dctx);
+    ret = nss_cmd_getbyid_session_recording_get_groups(dctx);
 
 done:
     return nss_cmd_done(cmdctx, ret);
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 810d5f8..005df47 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -63,6 +63,12 @@ struct nss_cmd_ctx {
 
     int saved_dom_idx;
     int saved_cur;
+
+    struct ldb_result **session_recording_groups; /**< NULL-terminated list of
+                                                       results of looking up
+                                                       groups for which
+                                                       session recording is
+                                                       enabled */
 };
 
 struct dom_ctx {
@@ -82,6 +88,13 @@ struct getent_ctx {
     char *name;
     char *domain;
     bool found;
+
+    /* Passwd-specific */
+    struct ldb_result **session_recording_groups; /**< NULL-terminated list of
+                                                       results of looking up
+                                                       groups for which
+                                                       session recording is
+                                                       enabled */
 };
 
 struct nss_dom_ctx {
@@ -119,6 +132,11 @@ struct setent_step_ctx {
 
     /* Netgroup-specific */
     char *name;
+
+    /* Passwd-specific */
+    bool need_session_recording_groups; /**< True if groups for which session
+                                             recording is enabled need to be
+                                             retrieved on the next step */
 };
 
 #define NSS_CMD_FATAL_ERROR(cctx) do { \
-- 
2.8.1

