From b2266c8c40fb84902ea88b6a306e72c640648a15 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstephen@redhat.com>
Date: Mon, 4 Sep 2017 16:26:24 -0400
Subject: [PATCH 1/2] SUDO: Rename sudo_threshold to sudo_rules_threshold

Change the name of the sudo threshold option to ensure it is
clear this option controls behavior related to sudo rules, not
commands or command groups. This will differentiate it from the
ipa_sudo_command_threshold option.
---
 src/confdb/confdb.h                        |  2 +-
 src/config/SSSDConfig/__init__.py.in       |  2 +-
 src/config/cfg_rules.ini                   |  2 +-
 src/config/etc/sssd.api.conf               |  2 +-
 src/man/sssd.conf.5.xml                    |  2 +-
 src/responder/sudo/sudosrv.c               |  4 ++--
 src/responder/sudo/sudosrv_get_sudorules.c | 12 ++++++------
 src/responder/sudo/sudosrv_private.h       |  2 +-
 8 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index bcea99ae4..4d103a64b 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -139,7 +139,7 @@
 #define CONFDB_DEFAULT_SUDO_TIMED false
 #define CONFDB_SUDO_INVERSE_ORDER "sudo_inverse_order"
 #define CONFDB_DEFAULT_SUDO_INVERSE_ORDER false
-#define CONFDB_SUDO_THRESHOLD "sudo_threshold"
+#define CONFDB_SUDO_RULES_THRESHOLD "sudo_rules_threshold"
 #define CONFDB_DEFAULT_SUDO_THRESHOLD 50
 
 /* autofs */
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 227f76180..3a860bd06 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -107,7 +107,7 @@ option_strings = {
     # [sudo]
     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
     'sudo_inverse_order' : _('If true, SSSD will switch back to lower-wins ordering logic'),
-    'sudo_threshold' : _('Maximum number of rules that can be refreshed at once. If this is exceeded, full refresh is performed.'),
+    'sudo_rules_threshold' : _('Maximum number of rules that can be refreshed at once. If this is exceeded, full refresh is performed.'),
 
     # [autofs]
     'autofs_negative_timeout' : _('Negative cache timeout length (seconds)'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 3e4ce4673..be0895887 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -147,7 +147,7 @@ option = cache_first
 # sudo service
 option = sudo_timed
 option = sudo_inverse_order
-option = sudo_threshold
+option = sudo_rules_threshold
 
 [rule/allowed_autofs_options]
 validator = ini_allowed_options
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 792c42a1f..bade9a8dd 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -79,7 +79,7 @@ pam_app_services = str, None, false
 # sudo service
 sudo_timed = bool, None, false
 sudo_inverse_order = bool, None, false
-sudo_threshold = int, None, false
+sudo_rules_threshold = int, None, false
 
 [autofs]
 # autofs service
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index c26f4a3ba..a2bec3746 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -1380,7 +1380,7 @@ pam_account_locked_message = Account locked, please contact help desk.
             </variablelist>
             <variablelist>
                 <varlistentry>
-                    <term>sudo_threshold (integer)</term>
+                    <term>sudo_rules_threshold (integer)</term>
                     <listitem>
                         <para>
                             Maximum number of expired rules that can be
diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
index dca70ea4a..1a0c8fec8 100644
--- a/src/responder/sudo/sudosrv.c
+++ b/src/responder/sudo/sudosrv.c
@@ -150,9 +150,9 @@ int sudo_process_init(TALLOC_CTX *mem_ctx,
 
     /* Get sudo_inverse_order option */
     ret = confdb_get_int(sudo_ctx->rctx->cdb,
-                         CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_THRESHOLD,
+                         CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_RULES_THRESHOLD,
                          CONFDB_DEFAULT_SUDO_THRESHOLD,
-                         &sudo_ctx->threshold);
+                         &sudo_ctx->rules_threshold);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
               ret, strerror(ret));
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
index 3272e634d..ed476a413 100644
--- a/src/responder/sudo/sudosrv_get_sudorules.c
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -479,7 +479,7 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
                            struct tevent_context *ev,
                            struct resp_ctx *rctx,
                            struct sss_domain_info *domain,
-                           int threshold,
+                           int rules_threshold,
                            uid_t uid,
                            const char *username,
                            char **groups)
@@ -521,10 +521,10 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
     DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules of [%s@%s]\n",
           num_rules, username, domain->name);
 
-    if (num_rules > threshold) {
+    if (num_rules > rules_threshold) {
         DEBUG(SSSDBG_TRACE_INTERNAL,
               "Rules threshold [%d] is reached, performing full refresh "
-              "instead.\n", threshold);
+              "instead.\n", rules_threshold);
 
         subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
                                          SSS_DP_SUDO_FULL_REFRESH,
@@ -621,7 +621,7 @@ struct sudosrv_get_rules_state {
     struct sss_domain_info *domain;
     char **groups;
     bool inverse_order;
-    int threshold;
+    int rules_threshold;
 
     struct sysdb_attrs **rules;
     uint32_t num_rules;
@@ -653,7 +653,7 @@ struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
     state->type = type;
     state->uid = uid;
     state->inverse_order = sudo_ctx->inverse_order;
-    state->threshold = sudo_ctx->threshold;
+    state->rules_threshold = sudo_ctx->rules_threshold;
 
     DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username);
 
@@ -710,7 +710,7 @@ static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq)
     }
 
     subreq = sudosrv_refresh_rules_send(state, state->ev, state->rctx,
-                                        state->domain, state->threshold,
+                                        state->domain, state->rules_threshold,
                                         state->uid, state->username,
                                         state->groups);
     if (subreq == NULL) {
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
index c76bdd395..a69e42065 100644
--- a/src/responder/sudo/sudosrv_private.h
+++ b/src/responder/sudo/sudosrv_private.h
@@ -48,7 +48,7 @@ struct sudo_ctx {
      */
     bool timed;
     bool inverse_order;
-    int threshold;
+    int rules_threshold;
 };
 
 struct sudo_cmd_ctx {

From ad2421ac47f448102636f7c45ae531384f3652cc Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstephen@redhat.com>
Date: Tue, 5 Sep 2017 10:29:37 -0400
Subject: [PATCH 2/2] IPA: Add threshold for sudo searches

Add a new option ipa_sudo_command_threshold option which will prevent
SSSD from creating large search filters for IPA sudo command and
command groups searches. The threshold will default to 50 and
if exceeded, a basic search filter will be used as a fallback to
retrieve all IPA sudo commands or command groups.

Resolves:
https://pagure.io/SSSD/sssd/issue/3507
---
 src/config/SSSDConfig/__init__.py.in    |  1 +
 src/config/cfg_rules.ini                |  1 +
 src/config/etc/sssd.api.d/sssd-ipa.conf |  1 +
 src/man/sssd-ipa.5.xml                  | 17 ++++++++++++++++
 src/providers/ipa/ipa_common.h          |  1 +
 src/providers/ipa/ipa_opts.c            |  2 ++
 src/providers/ipa/ipa_sudo.h            |  6 ++++++
 src/providers/ipa/ipa_sudo_async.c      | 35 ++++++++++++++++++++++++++++-----
 src/providers/ipa/ipa_sudo_conversion.c | 11 +++++++++++
 9 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 3a860bd06..c48465776 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -222,6 +222,7 @@ option_strings = {
     'ipa_deskprofile_search_base': _("Search base for Desktop Profile related objects"),
     'ipa_deskprofile_refresh': _("The amount of time in seconds between lookups of the Desktop Profile rules against the IPA server"),
     'ipa_deskprofile_request_interval': _("The amount of time in minutes between lookups of Desktop Profiles rules against the IPA server when the last request did not find any rule"),
+    'ipa_sudo_command_threshold': _("The total count of sudo commands or command groups to be retrieved in a single IPA search filter"),
 
     # [provider/ad]
     'ad_domain' : _('Active Directory domain'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index be0895887..45d770323 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -535,6 +535,7 @@ option = ipa_sudorule_sudoorder
 option = ipa_sudorule_usercategory
 option = ipa_sudorule_user
 option = ipa_sudorule_uuid
+option = ipa_sudo_command_threshold
 option = ipa_user_override_object_class
 option = ipa_view_class
 option = ipa_view_name
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index ab9634c7a..a14d7c67f 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -273,3 +273,4 @@ ipa_sudocmd_object_class = str, None, false
 ipa_sudocmd_uuid = str, None, false
 ipa_sudocmd_sudoCmd = str, None, false
 ipa_sudocmd_memberof = str, None, false
+ipa_sudo_command_threshold = int, None, false
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index 4cf07142c..bbb795390 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -553,6 +553,23 @@
                         <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/autofs_restart.xml" />
                     </listitem>
                 </varlistentry>
+
+                <varlistentry>
+                    <term>ipa_sudo_command_threshold (integer)</term>
+                    <listitem>
+                        <para>
+                            The total amount of sudo commands or command groups
+                            included in a search filter during retrieval of
+                            sudo commands from the IPA server. If the threshold
+                            is exceeded, SSSD will fallback to a simple search
+                            filter to retrieve all sudo commands or command groups.
+                        </para>
+                        <para>
+                            Default: 50
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
         <refsect2 id='views'>
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 5197a9af0..54bf8c3ba 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -59,6 +59,7 @@ enum ipa_basic_opt {
     IPA_DESKPROFILE_SEARCH_BASE,
     IPA_DESKPROFILE_REFRESH,
     IPA_DESKPROFILE_REQUEST_INTERVAL,
+    IPA_SUDO_COMMAND_THRESHOLD,
 
     IPA_OPTS_BASIC /* opts counter */
 };
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index 09b78f726..7f41502f5 100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -51,6 +51,8 @@ struct dp_option ipa_basic_opts[] = {
     { "ipa_deskprofile_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ipa_deskprofile_refresh", DP_OPT_NUMBER, { .number = 5 }, NULL_NUMBER },
     { "ipa_deskprofile_request_interval", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER },
+    { "ipa_sudo_command_threshold", DP_OPT_NUMBER, { .number = 50 }, NULL_NUMBER },
+
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_sudo.h b/src/providers/ipa/ipa_sudo.h
index 49b6c561b..8dae6fc87 100644
--- a/src/providers/ipa/ipa_sudo.h
+++ b/src/providers/ipa/ipa_sudo.h
@@ -105,6 +105,12 @@ ipa_sudo_conv_has_cmdgroups(struct ipa_sudo_conv *conv);
 bool
 ipa_sudo_conv_has_cmds(struct ipa_sudo_conv *conv);
 
+bool
+ipa_sudo_cmdgroups_exceed_threshold(struct ipa_sudo_conv *conv, int threshold);
+
+bool
+ipa_sudo_cmds_exceed_threshold(struct ipa_sudo_conv *conv, int threshold);
+
 char *
 ipa_sudo_conv_cmdgroup_filter(TALLOC_CTX *mem_ctx,
                               struct ipa_sudo_conv *conv);
diff --git a/src/providers/ipa/ipa_sudo_async.c b/src/providers/ipa/ipa_sudo_async.c
index 9ed121830..430c372c7 100644
--- a/src/providers/ipa/ipa_sudo_async.c
+++ b/src/providers/ipa/ipa_sudo_async.c
@@ -379,6 +379,7 @@ struct ipa_sudo_fetch_state {
     struct ipa_sudo_conv *conv;
     struct sysdb_attrs **rules;
     size_t num_rules;
+    int cmd_threshold;
     char *usn;
 };
 
@@ -404,7 +405,8 @@ ipa_sudo_fetch_send(TALLOC_CTX *mem_ctx,
                     struct sdap_attr_map *map_hostgroup,
                     struct sdap_handle *sh,
                     const char *cmdgroups_filter,
-                    const char *search_filter)
+                    const char *search_filter,
+                    int cmd_threshold)
 {
     struct ipa_sudo_fetch_state *state = NULL;
     struct tevent_req *req = NULL;
@@ -425,7 +427,7 @@ ipa_sudo_fetch_send(TALLOC_CTX *mem_ctx,
     state->sh = sh;
     state->search_filter = search_filter == NULL ? "" : search_filter;
     state->cmdgroups_filter = cmdgroups_filter;
-
+    state->cmd_threshold = cmd_threshold;
     state->map_cmdgroup = sudo_ctx->sudocmdgroup_map;
     state->map_rule = sudo_ctx->sudorule_map;
     state->map_cmd = sudo_ctx->sudocmd_map;
@@ -648,7 +650,16 @@ ipa_sudo_fetch_cmdgroups(struct tevent_req *req)
         return ipa_sudo_fetch_cmds(req);
     }
 
-    filter = ipa_sudo_conv_cmdgroup_filter(state, state->conv);
+    if (ipa_sudo_cmdgroups_exceed_threshold(state->conv, state->cmd_threshold)) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Command threshold [%d] exceeded, retrieving all sudo command "
+              "groups\n", state->cmd_threshold);
+        filter = talloc_asprintf(state, "(objectClass=%s)",
+                                 state->map_cmdgroup->name);
+    } else {
+        filter = ipa_sudo_conv_cmdgroup_filter(state, state->conv);
+    }
+
     if (filter == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build filter\n");
         return ENOMEM;
@@ -727,7 +738,16 @@ ipa_sudo_fetch_cmds(struct tevent_req *req)
         return EOK;
     }
 
-    filter = ipa_sudo_conv_cmd_filter(state, state->conv);
+    if (ipa_sudo_cmds_exceed_threshold(state->conv, state->cmd_threshold)) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Command threshold [%d] exceeded, retrieving all sudo commands\n",
+              state->cmd_threshold);
+        filter = talloc_asprintf(state, "(objectClass=%s)",
+                                 state->map_cmd->name);
+    } else {
+        filter = ipa_sudo_conv_cmd_filter(state, state->conv);
+    }
+
     if (filter == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build filter\n");
         return ENOMEM;
@@ -997,6 +1017,7 @@ ipa_sudo_refresh_host_done(struct tevent_req *subreq)
     struct ipa_hostinfo *host;
     struct tevent_req *req;
     int ret;
+    int cmd_threshold;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ipa_sudo_refresh_state);
@@ -1019,13 +1040,17 @@ ipa_sudo_refresh_host_done(struct tevent_req *subreq)
         return;
     }
 
+    cmd_threshold = dp_opt_get_int(state->ipa_opts->basic,
+                                   IPA_SUDO_COMMAND_THRESHOLD);
+
     subreq = ipa_sudo_fetch_send(state, state->ev, state->domain,
                                  state->sudo_ctx, host,
                                  state->sdap_opts->user_map,
                                  state->sdap_opts->group_map,
                                  state->ipa_opts->host_map,
                                  state->ipa_opts->hostgroup_map, state->sh,
-                                 state->cmdgroups_filter, state->search_filter);
+                                 state->cmdgroups_filter, state->search_filter,
+                                 cmd_threshold);
     if (subreq == NULL) {
         state->dp_error = DP_ERR_FATAL;
         tevent_req_error(req, ENOMEM);
diff --git a/src/providers/ipa/ipa_sudo_conversion.c b/src/providers/ipa/ipa_sudo_conversion.c
index f6d17d82b..402c2ea1d 100644
--- a/src/providers/ipa/ipa_sudo_conversion.c
+++ b/src/providers/ipa/ipa_sudo_conversion.c
@@ -576,6 +576,17 @@ ipa_sudo_conv_has_cmds(struct ipa_sudo_conv *conv)
     return hash_count(conv->cmds) == 0;
 }
 
+bool
+ipa_sudo_cmdgroups_exceed_threshold(struct ipa_sudo_conv *conv, int threshold)
+{
+        return (hash_count(conv->cmdgroups)) > threshold;
+}
+bool
+ipa_sudo_cmds_exceed_threshold(struct ipa_sudo_conv *conv, int threshold)
+{
+        return (hash_count(conv->cmds)) > threshold;
+}
+
 typedef errno_t (*ipa_sudo_conv_rdn_fn)(TALLOC_CTX *mem_ctx,
                                       struct sdap_attr_map *map,
                                       struct sysdb_ctx *sysdb,
