URL: https://github.com/SSSD/sssd/pull/705
Author: jhrozek
 Title: #705: KCM: Add configurable quotas
Action: opened

PR body:
"""
This PR adds several patches that let the user configure quotas to store
their ccaches.

Please see the commit messages, I hope they are verbose enough. One thing
that should be pointed out is that the global number of ccaches is explicitly
unlimited. Does anyone see an issue with just enforcing the per-UID limits?

An upcoming PR(s) would implement warning when the quota is being exceeded
and a sssctl command to let the administrator display the quota taken.
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/705/head:pr705
git checkout pr705
From 763fb7a5ef58834ab6d5fb02a7ecf7c9f719e8c8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 5 Oct 2018 13:17:14 +0200
Subject: [PATCH 1/8] MAN: Get rid of sssd-secrets reference

Related:
https://pagure.io/SSSD/sssd/issue/3685

There were some stray references to the secrets responder in the
sssd-kcm manual page.
---
 src/man/sssd-kcm.8.xml | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
index fff8b0a16..90b9ad09c 100644
--- a/src/man/sssd-kcm.8.xml
+++ b/src/man/sssd-kcm.8.xml
@@ -58,11 +58,9 @@
                 </listitem>
                 <listitem>
                     <para>
-                        the SSSD implementation stores the ccaches in the SSSD
-                        <citerefentry>
-                            <refentrytitle>sssd-secrets</refentrytitle><manvolnum>5</manvolnum>
-                        </citerefentry>
-                        secrets store, allowing the ccaches to survive KCM server restarts or machine reboots.
+                        the SSSD implementation stores the ccaches in a database,
+                        typically located at <replaceable>/var/lib/sss/secrets</replaceable>
+                        allowing the ccaches to survive KCM server restarts or machine reboots.
                     </para>
                 </listitem>
             </itemizedlist>

From a3171af55e7fa88bae586d84d53ddb8f8c5d13a8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 30 Nov 2018 13:15:58 +0100
Subject: [PATCH 2/8] MAN: Document that it is enough to systemctl restart
 sssd-kcm.service lately

Related:
https://pagure.io/SSSD/sssd/issue/3862

We forgot to amend the man page after implementing the sssd-kcm service
reload.
---
 src/man/sssd-kcm.8.xml | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
index 90b9ad09c..4e4aaa38e 100644
--- a/src/man/sssd-kcm.8.xml
+++ b/src/man/sssd-kcm.8.xml
@@ -162,12 +162,17 @@ systemctl restart sssd-kcm.service
         <title>CONFIGURATION OPTIONS</title>
         <para>
             The KCM service is configured in the <quote>kcm</quote>
-            section of the sssd.conf file. Please note that currently,
-            is it not sufficient to restart the sssd-kcm service, because
-            the sssd configuration is only parsed and read to an internal
-            configuration database by the sssd service. Therefore you
-            must restart the sssd service if you change anything in the
-            <quote>kcm</quote> section of sssd.conf.
+            section of the sssd.conf file. Please note that because
+            the KCM service is typically socket-activated, it is
+            enough to just restart the <quote>sssd-kcm</quote> service
+            after changing options in the <quote>kcm</quote> section
+            of sssd.conf:
+            <programlisting>
+systemctl restart sssd-kcm.service
+            </programlisting>
+        </para>
+        <para>
+            The KCM service is configured in the <quote>kcm</quote>
             For a detailed syntax reference, refer to the <quote>FILE FORMAT</quote> section of the
             <citerefentry>
                 <refentrytitle>sssd.conf</refentrytitle>

From 9f5455a41271694ac987677df9fdffe3ebb8edb8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Mon, 26 Nov 2018 13:44:08 +0100
Subject: [PATCH 3/8] SECRETS: Use different option names from secrets and KCM
 for quota options

Related:
https://pagure.io/SSSD/sssd/issue/3386

With the separate secrets responder, the quotas for the /secrets and
/kcm hives were configurable in a sub-section of the [secrets] sssd.conf
section using the same option -- the /secrets vs. /kcm distinction was
made using the subsection name.

With the standalone KCM responder writing directly to the database, it
makes sense to have options with more descriptive names better suitable
for the KCM usage. For that we need the options for secrets quotas and
kcm quotas to be named differently.

For now, the patch only passes the option name to sss_sec_get_quota()
and sss_sec_get_hive_config() together with the default value in an
instance of a new structure sss_sec_quota_opt. The secrets responder
still uses the same option names for backwards compatibility.
---
 src/responder/secrets/secsrv.c | 57 +++++++++++++++++++++++++++-------
 src/util/secrets/config.c      | 40 ++++++++++++------------
 src/util/secrets/secrets.h     | 21 ++++++++-----
 3 files changed, 78 insertions(+), 40 deletions(-)

diff --git a/src/responder/secrets/secsrv.c b/src/responder/secrets/secsrv.c
index 2de93dedc..b18bbfd19 100644
--- a/src/responder/secrets/secsrv.c
+++ b/src/responder/secrets/secsrv.c
@@ -47,6 +47,39 @@ static void adjust_global_quota(struct sec_ctx *sctx,
 static int sec_get_config(struct sec_ctx *sctx)
 {
     int ret;
+    struct sss_sec_quota_opt dfl_sec_nest_level = {
+        .opt_name = CONFDB_SEC_CONTAINERS_NEST_LEVEL,
+        .default_value = DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
+    };
+    struct sss_sec_quota_opt dfl_sec_max_secrets = {
+        .opt_name = CONFDB_SEC_MAX_SECRETS,
+        .default_value = DEFAULT_SEC_MAX_SECRETS,
+    };
+    struct sss_sec_quota_opt dfl_sec_max_uid_secrets = {
+        .opt_name = CONFDB_SEC_MAX_UID_SECRETS,
+        .default_value = DEFAULT_SEC_MAX_UID_SECRETS,
+    };
+    struct sss_sec_quota_opt dfl_sec_max_payload_size = {
+        .opt_name = CONFDB_SEC_MAX_PAYLOAD_SIZE,
+        .default_value = DEFAULT_SEC_MAX_PAYLOAD_SIZE,
+    };
+
+    struct sss_sec_quota_opt dfl_kcm_nest_level = {
+        .opt_name = CONFDB_SEC_CONTAINERS_NEST_LEVEL,
+        .default_value = DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
+    };
+    struct sss_sec_quota_opt dfl_kcm_max_secrets = {
+        .opt_name = CONFDB_SEC_MAX_SECRETS,
+        .default_value = DEFAULT_SEC_KCM_MAX_SECRETS,
+    };
+    struct sss_sec_quota_opt dfl_kcm_max_uid_secrets = {
+        .opt_name = CONFDB_SEC_MAX_UID_SECRETS,
+        .default_value = DEFAULT_SEC_KCM_MAX_UID_SECRETS,
+    };
+    struct sss_sec_quota_opt dfl_kcm_max_payload_size = {
+        .opt_name = CONFDB_SEC_MAX_PAYLOAD_SIZE,
+        .default_value = DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE,
+    };
 
     ret = confdb_get_int(sctx->rctx->cdb,
                          sctx->rctx->confdb_service_path,
@@ -70,10 +103,10 @@ static int sec_get_config(struct sec_ctx *sctx)
      */
     ret = sss_sec_get_quota(sctx->rctx->cdb,
                             sctx->rctx->confdb_service_path,
-                            DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
-                            DEFAULT_SEC_MAX_SECRETS,
-                            DEFAULT_SEC_MAX_UID_SECRETS,
-                            DEFAULT_SEC_MAX_PAYLOAD_SIZE,
+                            &dfl_sec_nest_level,
+                            &dfl_sec_max_secrets,
+                            &dfl_sec_max_uid_secrets,
+                            &dfl_sec_max_payload_size,
                             &sctx->sec_config.quota);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
@@ -84,10 +117,10 @@ static int sec_get_config(struct sec_ctx *sctx)
     /* Read the per-hive configuration */
     ret = sss_sec_get_hive_config(sctx->rctx->cdb,
                                  "secrets",
-                                 sctx->sec_config.quota.containers_nest_level,
-                                 sctx->sec_config.quota.max_secrets,
-                                 sctx->sec_config.quota.max_uid_secrets,
-                                 sctx->sec_config.quota.max_payload_size,
+                                 &dfl_sec_nest_level,
+                                 &dfl_sec_max_secrets,
+                                 &dfl_sec_max_uid_secrets,
+                                 &dfl_sec_max_payload_size,
                                  &sctx->sec_config);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
@@ -98,10 +131,10 @@ static int sec_get_config(struct sec_ctx *sctx)
 
     ret = sss_sec_get_hive_config(sctx->rctx->cdb,
                                   "kcm",
-                                  DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
-                                  DEFAULT_SEC_KCM_MAX_SECRETS,
-                                  DEFAULT_SEC_KCM_MAX_UID_SECRETS,
-                                  DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE,
+                                  &dfl_kcm_nest_level,
+                                  &dfl_kcm_max_secrets,
+                                  &dfl_kcm_max_uid_secrets,
+                                  &dfl_kcm_max_payload_size,
                                   &sctx->kcm_config);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
diff --git a/src/util/secrets/config.c b/src/util/secrets/config.c
index cb286121f..f5dac0b21 100644
--- a/src/util/secrets/config.c
+++ b/src/util/secrets/config.c
@@ -24,10 +24,10 @@
 
 errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
                           const char *section_config_path,
-                          int default_max_containers_nest_level,
-                          int default_max_num_secrets,
-                          int default_max_num_uid_secrets,
-                          int default_max_payload,
+                          struct sss_sec_quota_opt *dfl_max_containers_nest_level,
+                          struct sss_sec_quota_opt *dfl_max_num_secrets,
+                          struct sss_sec_quota_opt *dfl_max_num_uid_secrets,
+                          struct sss_sec_quota_opt *dfl_max_payload,
                           struct sss_sec_quota *quota)
 {
     int ret;
@@ -38,8 +38,8 @@ errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
 
     ret = confdb_get_int(cdb,
                          section_config_path,
-                         CONFDB_SEC_CONTAINERS_NEST_LEVEL,
-                         default_max_containers_nest_level,
+                         dfl_max_containers_nest_level->opt_name,
+                         dfl_max_containers_nest_level->default_value,
                          &quota->containers_nest_level);
 
     if (ret != EOK) {
@@ -51,8 +51,8 @@ errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
 
     ret = confdb_get_int(cdb,
                          section_config_path,
-                         CONFDB_SEC_MAX_SECRETS,
-                         default_max_num_secrets,
+                         dfl_max_num_secrets->opt_name,
+                         dfl_max_num_secrets->default_value,
                          &quota->max_secrets);
 
     if (ret != EOK) {
@@ -64,8 +64,8 @@ errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
 
     ret = confdb_get_int(cdb,
                          section_config_path,
-                         CONFDB_SEC_MAX_UID_SECRETS,
-                         default_max_num_uid_secrets,
+                         dfl_max_num_uid_secrets->opt_name,
+                         dfl_max_num_uid_secrets->default_value,
                          &quota->max_uid_secrets);
 
     if (ret != EOK) {
@@ -77,8 +77,8 @@ errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
 
     ret = confdb_get_int(cdb,
                          section_config_path,
-                         CONFDB_SEC_MAX_PAYLOAD_SIZE,
-                         default_max_payload,
+                         dfl_max_payload->opt_name,
+                         dfl_max_payload->default_value,
                          &quota->max_payload_size);
 
     if (ret != EOK) {
@@ -93,10 +93,10 @@ errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
 
 errno_t sss_sec_get_hive_config(struct confdb_ctx *cdb,
                                 const char *hive_name,
-                                int default_max_containers_nest_level,
-                                int default_max_num_secrets,
-                                int default_max_num_uid_secrets,
-                                int default_max_payload,
+                                struct sss_sec_quota_opt *dfl_max_containers_nest_level,
+                                struct sss_sec_quota_opt *dfl_max_num_secrets,
+                                struct sss_sec_quota_opt *dfl_max_num_uid_secrets,
+                                struct sss_sec_quota_opt *dfl_max_payload,
                                 struct sss_sec_hive_config *hive_config)
 {
     int ret;
@@ -122,10 +122,10 @@ errno_t sss_sec_get_hive_config(struct confdb_ctx *cdb,
 
     ret = sss_sec_get_quota(cdb,
                             confdb_section,
-                            default_max_containers_nest_level,
-                            default_max_num_secrets,
-                            default_max_num_uid_secrets,
-                            default_max_payload,
+                            dfl_max_containers_nest_level,
+                            dfl_max_num_secrets,
+                            dfl_max_num_uid_secrets,
+                            dfl_max_payload,
                             &hive_config->quota);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
diff --git a/src/util/secrets/secrets.h b/src/util/secrets/secrets.h
index 01abfe542..31164bd86 100644
--- a/src/util/secrets/secrets.h
+++ b/src/util/secrets/secrets.h
@@ -47,6 +47,11 @@ struct sss_sec_ctx;
 
 struct sss_sec_req;
 
+struct sss_sec_quota_opt {
+    const char *opt_name;
+    int default_value;
+};
+
 struct sss_sec_quota {
     int max_secrets;
     int max_uid_secrets;
@@ -98,18 +103,18 @@ bool sss_sec_req_is_list(struct sss_sec_req *req);
 
 errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
                           const char *section_config_path,
-                          int default_max_containers_nest_level,
-                          int default_max_num_secrets,
-                          int default_max_num_uid_secrets,
-                          int default_max_payload,
+                          struct sss_sec_quota_opt *dfl_max_containers_nest_level,
+                          struct sss_sec_quota_opt *dfl_max_num_secrets,
+                          struct sss_sec_quota_opt *dfl_max_num_uid_secrets,
+                          struct sss_sec_quota_opt *dfl_max_payload,
                           struct sss_sec_quota *quota);
 
 errno_t sss_sec_get_hive_config(struct confdb_ctx *cdb,
                                 const char *hive_name,
-                                int default_max_containers_nest_level,
-                                int default_max_num_secrets,
-                                int default_max_num_uid_secrets,
-                                int default_max_payload,
+                                struct sss_sec_quota_opt *dfl_max_containers_nest_level,
+                                struct sss_sec_quota_opt *dfl_max_num_secrets,
+                                struct sss_sec_quota_opt *dfl_max_num_uid_secrets,
+                                struct sss_sec_quota_opt *dfl_max_payload,
                                 struct sss_sec_hive_config *hive_config);
 
 #endif /* __SECRETS_H_ */

From b2d74a88a751dead4c7dedde55e43bf693716a8e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 30 Nov 2018 13:34:22 +0100
Subject: [PATCH 4/8] SECRETS: Don't limit the global number of ccaches

Related:
https://pagure.io/SSSD/sssd/issue/3386

In the KCM context, the global number of ccaches would limit the number
of users who can store their ccaches in the KCM deamon.

In more detail, the options have the following semantics with KCM:
    - DEFAULT_SEC_KCM_MAX_SECRETS - global number of secrets, would
    cover both how many ccaches can a user store, but this is better
    served with DEFAULT_SEC_KCM_MAX_UID_SECRETS

    - DEFAULT_SEC_KCM_MAX_UID_SECRETS - how many 'principals' can a user
    kinit with

    - DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE - the payload size of service
    tickets

With the above in mind, I think the most important limits are
max_uid_secrets to limit and the payload size to constraint how much
space can a user occupy and it doesn't make much sense to limit the
global quota.
---
 src/util/secrets/secrets.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/util/secrets/secrets.h b/src/util/secrets/secrets.h
index 31164bd86..9cf397516 100644
--- a/src/util/secrets/secrets.h
+++ b/src/util/secrets/secrets.h
@@ -39,7 +39,7 @@
  * but the secret size must be large because one secret in the /kcm
  * hive holds the whole ccache which consists of several credentials
  */
-#define DEFAULT_SEC_KCM_MAX_SECRETS      256
+#define DEFAULT_SEC_KCM_MAX_SECRETS      0          /* unlimited */
 #define DEFAULT_SEC_KCM_MAX_UID_SECRETS  64
 #define DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE 65536
 

From 421c7f91048e488d6b1b702f76497711ba3928d6 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 28 Nov 2018 21:24:08 +0100
Subject: [PATCH 5/8] KCM: Pass confdb context to the ccache db initialization

Resolves:
https://pagure.io/SSSD/sssd/issue/3386

The libsecrets back end needs to read the quota options from confdb,
therefore it needs to know the section and access the confdb handle.

These parameters are unused for other ccache back end types, but they
are harmless and IMO it makes more sense to keep the ccache back end
abstract.
---
 src/responder/kcm/kcm.c                   | 14 ++++++++++++--
 src/responder/kcm/kcmsrv_ccache.c         |  4 +++-
 src/responder/kcm/kcmsrv_ccache.h         |  2 ++
 src/responder/kcm/kcmsrv_ccache_be.h      |  4 +++-
 src/responder/kcm/kcmsrv_ccache_mem.c     |  4 +++-
 src/responder/kcm/kcmsrv_ccache_secdb.c   |  6 +++---
 src/responder/kcm/kcmsrv_ccache_secrets.c |  4 +++-
 7 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
index 005dd168f..045c7801f 100644
--- a/src/responder/kcm/kcm.c
+++ b/src/responder/kcm/kcm.c
@@ -170,6 +170,8 @@ static int kcm_data_destructor(void *ptr)
 
 static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
                                            struct tevent_context *ev,
+                                           struct confdb_ctx *cdb,
+                                           const char *confdb_service_path,
                                            enum kcm_ccdb_be cc_be)
 {
     struct kcm_resp_ctx *kcm_data;
@@ -181,7 +183,11 @@ static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
-    kcm_data->db = kcm_ccdb_init(kcm_data, ev, cc_be);
+    kcm_data->db = kcm_ccdb_init(kcm_data,
+                                 ev,
+                                 cdb,
+                                 confdb_service_path,
+                                 cc_be);
     if (kcm_data->db == NULL) {
         talloc_free(kcm_data);
         return NULL;
@@ -235,7 +241,11 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
-    kctx->kcm_data = kcm_data_setup(kctx, ev, kctx->cc_be);
+    kctx->kcm_data = kcm_data_setup(kctx,
+                                    ev,
+                                    kctx->rctx->cdb,
+                                    kctx->rctx->confdb_service_path,
+                                    kctx->cc_be);
     if (kctx->kcm_data == NULL) {
         DEBUG(SSSDBG_FATAL_FAILURE,
               "fatal error initializing responder data\n");
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
index af2bcf8bb..2009c71e0 100644
--- a/src/responder/kcm/kcmsrv_ccache.c
+++ b/src/responder/kcm/kcmsrv_ccache.c
@@ -227,6 +227,8 @@ struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd)
 
 struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
                                struct tevent_context *ev,
+                               struct confdb_ctx *cdb,
+                               const char *confdb_service_path,
                                enum kcm_ccdb_be cc_be)
 {
     errno_t ret;
@@ -268,7 +270,7 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
-    ret = ccdb->ops->init(ccdb);
+    ret = ccdb->ops->init(ccdb, cdb, confdb_service_path);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize ccache database\n");
         talloc_free(ccdb);
diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
index 0fd33325f..199b75b16 100644
--- a/src/responder/kcm/kcmsrv_ccache.h
+++ b/src/responder/kcm/kcmsrv_ccache.h
@@ -125,6 +125,8 @@ struct kcm_ccdb;
  */
 struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
                                struct tevent_context *ev,
+                               struct confdb_ctx *cdb,
+                               const char *confdb_service_path,
                                enum kcm_ccdb_be cc_be);
 
 /*
diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h
index 7315f6435..166af3a76 100644
--- a/src/responder/kcm/kcmsrv_ccache_be.h
+++ b/src/responder/kcm/kcmsrv_ccache_be.h
@@ -30,7 +30,9 @@
 #include "responder/kcm/kcmsrv_ccache.h"
 
 typedef errno_t
-(*ccdb_init_fn)(struct kcm_ccdb *db);
+(*ccdb_init_fn)(struct kcm_ccdb *db,
+                struct confdb_ctx *cdb,
+                const char *confdb_service_path);
 
 typedef struct tevent_req *
 (*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx,
diff --git a/src/responder/kcm/kcmsrv_ccache_mem.c b/src/responder/kcm/kcmsrv_ccache_mem.c
index 38bc2050d..35955b2f4 100644
--- a/src/responder/kcm/kcmsrv_ccache_mem.c
+++ b/src/responder/kcm/kcmsrv_ccache_mem.c
@@ -151,7 +151,9 @@ static int ccwrap_destructor(void *ptr)
     return 0;
 }
 
-static errno_t ccdb_mem_init(struct kcm_ccdb *db)
+static errno_t ccdb_mem_init(struct kcm_ccdb *db,
+                             struct confdb_ctx *cdb,
+                             const char *confdb_service_path)
 {
     struct ccdb_mem *memdb = NULL;
 
diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c
index a61d7b15b..5ccb21aa7 100644
--- a/src/responder/kcm/kcmsrv_ccache_secdb.c
+++ b/src/responder/kcm/kcmsrv_ccache_secdb.c
@@ -520,7 +520,9 @@ static errno_t secdb_get_cc(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
-static errno_t ccdb_secdb_init(struct kcm_ccdb *db)
+static errno_t ccdb_secdb_init(struct kcm_ccdb *db,
+                               struct confdb_ctx *cdb,
+                               const char *confdb_service_path)
 {
     struct ccdb_secdb *secdb = NULL;
     errno_t ret;
@@ -530,8 +532,6 @@ static errno_t ccdb_secdb_init(struct kcm_ccdb *db)
         return ENOMEM;
     }
 
-    /* TODO: read configuration from the config file, adjust quotas */
-
     ret = sss_sec_init(db, NULL, &secdb->sctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
diff --git a/src/responder/kcm/kcmsrv_ccache_secrets.c b/src/responder/kcm/kcmsrv_ccache_secrets.c
index 4c52497d4..7b019fded 100644
--- a/src/responder/kcm/kcmsrv_ccache_secrets.c
+++ b/src/responder/kcm/kcmsrv_ccache_secrets.c
@@ -659,7 +659,9 @@ static errno_t sec_get_ccache_recv(struct tevent_req *req,
 /*
  * The actual sssd-secrets back end
  */
-static errno_t ccdb_sec_init(struct kcm_ccdb *db)
+static errno_t ccdb_sec_init(struct kcm_ccdb *db,
+                             struct confdb_ctx *cdb,
+                             const char *confdb_service_path)
 {
     struct ccdb_sec *secdb = NULL;
 

From 6ce204b5002044c08e541c9dc33587ba3a0ada69 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 28 Nov 2018 21:22:22 +0100
Subject: [PATCH 6/8] KCM: Configurable quotas for the secdb ccache back end

Related:
https://pagure.io/SSSD/sssd/issue/3386

Exposes three new options for the [kcm] responder to set the global
ccache limit, the per-uid ccache limit and the payload size.
---
 src/confdb/confdb.h                     |  3 ++
 src/config/cfg_rules.ini                |  3 ++
 src/man/sssd-kcm.8.xml                  | 37 +++++++++++++++
 src/responder/kcm/kcmsrv_ccache_secdb.c | 61 ++++++++++++++++++++++++-
 4 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 741d4bc47..612d3181d 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -266,6 +266,9 @@
 #define CONFDB_KCM_CONF_ENTRY "config/kcm"
 #define CONFDB_KCM_SOCKET "socket_path"
 #define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */
+#define CONFDB_KCM_MAX_CCACHES "max_ccaches"
+#define CONFDB_KCM_MAX_UID_CCACHES "max_uid_ccaches"
+#define CONFDB_KCM_MAX_CCACHE_SIZE "max_ccache_size"
 
 /* Certificate mapping rules */
 #define CONFDB_CERTMAP_BASEDN "cn=certmap,cn=config"
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 09a52df4e..76941f504 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -311,6 +311,9 @@ option = description
 option = socket_path
 option = ccache_storage
 option = responder_idle_timeout
+option = max_ccaches
+option = max_uid_ccaches
+option = max_ccache_size
 
 # Session recording
 [rule/allowed_session_recording_options]
diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
index 4e4aaa38e..2f66e56a4 100644
--- a/src/man/sssd-kcm.8.xml
+++ b/src/man/sssd-kcm.8.xml
@@ -201,6 +201,43 @@ systemctl restart sssd-kcm.service
                     </para>
                 </listitem>
             </varlistentry>
+            <varlistentry>
+                <term>max_ccaches (integer)</term>
+                <listitem>
+                    <para>
+                        How many credential caches does the KCM database allow
+                        for all users.
+                    </para>
+                    <para>
+                        Default: 0 (unlimited, only the per-UID quota is enforced)
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>max_uid_ccaches (integer)</term>
+                <listitem>
+                    <para>
+                        How many credential caches does the KCM database allow
+                        per UID. This is equivalent to <quote>with how many
+                        principals you can kinit</quote>.
+                    </para>
+                    <para>
+                        Default: 64
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>max_ccache_size (integer)</term>
+                <listitem>
+                    <para>
+                        How big can a credential cache be per ccache. Each
+                        service ticket accounts into this quota.
+                    </para>
+                    <para>
+                        Default: 65536
+                    </para>
+                </listitem>
+            </varlistentry>
         </variablelist>
     </refsect1>
 
diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c
index 5ccb21aa7..26ee1032d 100644
--- a/src/responder/kcm/kcmsrv_ccache_secdb.c
+++ b/src/responder/kcm/kcmsrv_ccache_secdb.c
@@ -526,13 +526,72 @@ static errno_t ccdb_secdb_init(struct kcm_ccdb *db,
 {
     struct ccdb_secdb *secdb = NULL;
     errno_t ret;
+    struct sss_sec_hive_config **kcm_section_quota;
+    struct sss_sec_quota_opt dfl_kcm_nest_level = {
+        .opt_name = CONFDB_SEC_CONTAINERS_NEST_LEVEL,
+        .default_value = DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
+    };
+    struct sss_sec_quota_opt dfl_kcm_max_secrets = {
+        .opt_name = CONFDB_KCM_MAX_CCACHES,
+        .default_value = DEFAULT_SEC_KCM_MAX_SECRETS,
+    };
+    struct sss_sec_quota_opt dfl_kcm_max_uid_secrets = {
+        .opt_name = CONFDB_KCM_MAX_UID_CCACHES,
+        .default_value = DEFAULT_SEC_KCM_MAX_UID_SECRETS,
+    };
+    struct sss_sec_quota_opt dfl_kcm_max_payload_size = {
+        .opt_name = CONFDB_KCM_MAX_CCACHE_SIZE,
+        .default_value = DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE,
+    };
+
 
     secdb = talloc_zero(db, struct ccdb_secdb);
     if (secdb == NULL) {
         return ENOMEM;
     }
 
-    ret = sss_sec_init(db, NULL, &secdb->sctx);
+    kcm_section_quota = talloc_zero_array(secdb,
+                                          struct sss_sec_hive_config *,
+                                          2);
+    if (kcm_section_quota == NULL) {
+        talloc_free(secdb);
+        return ENOMEM;
+    }
+
+    kcm_section_quota[0] = talloc_zero(kcm_section_quota,
+                                       struct sss_sec_hive_config);
+    if (kcm_section_quota == NULL) {
+        talloc_free(secdb);
+        return ENOMEM;
+    }
+    kcm_section_quota[0]->hive_name = "kcm";
+
+    ret = sss_sec_get_quota(cdb,
+                            confdb_service_path,
+                            &dfl_kcm_nest_level,
+                            &dfl_kcm_max_secrets,
+                            &dfl_kcm_max_uid_secrets,
+                            &dfl_kcm_max_payload_size,
+                            &kcm_section_quota[0]->quota);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to get KCM global quotas [%d]: %s\n",
+              ret, sss_strerror(ret));
+        talloc_free(secdb);
+        return ret;
+    }
+
+    if (kcm_section_quota[0]->quota.max_uid_secrets > 0) {
+        /* Even cn=default is considered a secret that adds up to
+         * the quota. To avoid off-by-one-confusion, increase
+         * the quota by two to 1) account for the cn=default object
+         * and 2) always allow writing to cn=defaults even if we
+         * are exactly at the quota limit
+         */
+        kcm_section_quota[0]->quota.max_uid_secrets += 2;
+    }
+
+    ret = sss_sec_init(db, kcm_section_quota, &secdb->sctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Cannot initialize the security database\n");

From fa5451e1c28ea827f749b525cb4b5c8ac30c81fd Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 27 Nov 2018 11:39:18 +0100
Subject: [PATCH 7/8] MULTIHOST: Do not use the deprecated namespace

This issue was causing warnings with the current pytest versions as
installed from pip.

See:
https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace
---
 src/tests/multihost/basic/conftest.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/tests/multihost/basic/conftest.py b/src/tests/multihost/basic/conftest.py
index 65e2d641b..a9e9cf0a6 100644
--- a/src/tests/multihost/basic/conftest.py
+++ b/src/tests/multihost/basic/conftest.py
@@ -17,13 +17,13 @@
 import ldap
 
 
-def pytest_namespace():
-    return {'num_masters': 1,
-            'num_ad': 0,
-            'num_atomic': 0,
-            'num_replicas': 0,
-            'num_clients': 0,
-            'num_others': 0}
+def pytest_configure():
+    pytest.num_masters = 1
+    pytest.num_ad = 0
+    pytest.num_atomic = 0
+    pytest.num_replicas = 0
+    pytest.num_clients = 0
+    pytest.num_others = 0
 
 
 @pytest.fixture(scope="class")

From 7aed2077ed33c8ad2c252aabb8da19a22344486a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Mon, 26 Nov 2018 21:10:21 +0100
Subject: [PATCH 8/8] TESTS: Add tests for the configurable quotas

Related:
https://pagure.io/SSSD/sssd/issue/3386
---
 src/tests/multihost/basic/conftest.py |   8 +++
 src/tests/multihost/basic/test_kcm.py | 100 ++++++++++++++++++++++++++
 2 files changed, 108 insertions(+)

diff --git a/src/tests/multihost/basic/conftest.py b/src/tests/multihost/basic/conftest.py
index a9e9cf0a6..fd12c1b5b 100644
--- a/src/tests/multihost/basic/conftest.py
+++ b/src/tests/multihost/basic/conftest.py
@@ -397,6 +397,14 @@ def create_posix_usersgroups(session_multihost):
         assert ret == 'Success'
 
 
+@pytest.fixture(scope='session')
+def create_many_user_principals(session_multihost):
+    krb = krb5srv(session_multihost.master[0], 'EXAMPLE.TEST')
+    for i in range(1, 65):
+        username = "user%04d" % i
+        krb.add_principal(username, 'user', 'Secret123')
+
+
 @pytest.fixture(scope="session", autouse=True)
 def setup_session(request, session_multihost,
                   package_install,
diff --git a/src/tests/multihost/basic/test_kcm.py b/src/tests/multihost/basic/test_kcm.py
index 87e325bd7..f463a1260 100644
--- a/src/tests/multihost/basic/test_kcm.py
+++ b/src/tests/multihost/basic/test_kcm.py
@@ -3,6 +3,7 @@
 import paramiko
 import pytest
 import os
+import time
 from utils_config import set_param, remove_section
 
 
@@ -122,3 +123,102 @@ def test_kcm_debug_level_set(self, multihost, enable_kcm):
 
         log_lines_debug = self._kcm_log_length(multihost)
         assert log_lines_debug > log_lines_pre + 100
+
+
+    def test_kcm_peruid_quota(self,
+                             multihost,
+                             enable_kcm,
+                             create_many_user_principals):
+        """
+        Make sure the quota limits a client, but only that client
+        """
+        ssh_foo2 = SSHClient(multihost.master[0].sys_hostname,
+                             username='foo2', password='Secret123')
+        ssh_foo3 = SSHClient(multihost.master[0].sys_hostname,
+                             username='foo3', password='Secret123')
+
+        # The loop would request 63 users, plus there is foo3 we authenticated
+        # earlier, so this should exactly deplete the quota, but should succeed
+        for i in range(1, 64):
+            username = "user%04d" % i
+            (_, _, exit_status) = ssh_foo3.execute_cmd('kinit %s' % username,
+                                                  stdin='Secret123')
+            assert exit_status == 0
+
+        # this kinit should be exactly one over the peruid limit
+        (_, _, exit_status) = ssh_foo3.execute_cmd('kinit user0064',
+                                                   stdin='Secret123')
+        assert exit_status != 0
+
+        # Since this is a per-uid limit, another user should be able to kinit
+        # just fine
+        (_, _, exit_status) = ssh_foo2.execute_cmd('kinit user0064',
+                                                   stdin='Secret123')
+        assert exit_status == 0
+
+        # kdestroy as the original user, the quota should allow a subsequent
+        # kinit
+        ssh_foo3.execute_cmd('kdestroy -A')
+        (_, _, exit_status) = ssh_foo3.execute_cmd('kinit user0064',
+                                                   stdin='Secret123')
+        assert exit_status == 0
+
+        ssh_foo2.close()
+        ssh_foo3.close()
+
+
+    def test_kcm_peruid_quota_increase(self,
+                                       multihost,
+                                       enable_kcm,
+                                       create_many_user_principals):
+        """
+        Increasing the peruid quota allows a client to store more
+        data
+        """
+        ssh_foo3 = SSHClient(multihost.master[0].sys_hostname,
+                             username='foo3', password='Secret123')
+
+        # The loop would request 63 users, plus there is foo3 we authenticated
+        # earlier, so this should exactly deplete the quota, but should succeed
+        for i in range(1, 64):
+            username = "user%04d" % i
+            (_, _, exit_status) = ssh_foo3.execute_cmd('kinit %s' % username,
+                                                  stdin='Secret123')
+            assert exit_status == 0
+
+        # this kinit should be exactly one over the peruid limit
+        (_, _, exit_status) = ssh_foo3.execute_cmd('kinit user0064',
+                                                   stdin='Secret123')
+        assert exit_status != 0
+
+        set_param(multihost, 'kcm', 'max_uid_ccaches', '65')
+        self._restart_kcm(multihost)
+
+        # Now the kinit should work as we increased the limit
+        (_, _, exit_status) = ssh_foo3.execute_cmd('kinit user0064',
+                                                   stdin='Secret123')
+        assert exit_status == 0
+
+        ssh.execute_cmd('kdestroy -A')
+        ssh_foo3.close()
+
+
+    def test_kcm_payload_low_quota(self,
+                                   multihost,
+                                   enable_kcm):
+        """
+        Set a prohibitive quota for the per-ccache payload limit and
+        make sure it gets enforced
+        """
+
+        ssh_foo3 = SSHClient(multihost.master[0].sys_hostname,
+                             username='foo3', password='Secret123')
+        ssh_foo3.execute_cmd('kdestroy -A')
+        ssh_foo3.close()
+
+        set_param(multihost, 'kcm', 'max_ccache_size', '1')
+        self._restart_kcm(multihost)
+
+        with pytest.raises(paramiko.ssh_exception.AuthenticationException):
+            ssh_foo3 = SSHClient(multihost.master[0].sys_hostname,
+                                 username='foo3', password='Secret123')
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to