URL: https://github.com/SSSD/sssd/pull/5494
Author: abbra
 Title: #5494: pam_sss_gss: support authentication indicators
Action: opened

PR body:
"""
MIT Kerberos allows to associate authentication indicators with the
issued ticket based on the way how the TGT was obtained. The indicators
present in the TGT then copied to service tickets. There are two ways to
check the authentication indicators:

 - when KDC issues a service ticket, a policy at KDC side can reject the
   ticket issuance based on a lack of certain indicator

 - when a server application presented with a service ticket from a
   client, it can verify that this ticket contains intended
   authentication indicators before authorizing access from the client.

Add support to validate presence of a specific (set of) authentication
indicator(s) in pam_sss_gss when validating a user's TGT.

This concept can be used to only allow access to a PAM service when user
is in possession of a ticket obtained using some of pre-authentication
mechanisms that require multiple factors: smart-cards (PKINIT), 2FA
tokens (otp/radius), etc.

Resolves: https://github.com/SSSD/sssd/issues/5482
Signed-off-by: Alexander Bokovoy <aboko...@redhat.com>
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/5494/head:pr5494
git checkout pr5494
From a2185e7e100fc7ca8792f538cb5aa8cc08e7dc74 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
Date: Fri, 5 Feb 2021 20:36:27 +0200
Subject: [PATCH] pam_sss_gss: support authentication indicators

MIT Kerberos allows to associate authentication indicators with the
issued ticket based on the way how the TGT was obtained. The indicators
present in the TGT then copied to service tickets. There are two ways to
check the authentication indicators:

 - when KDC issues a service ticket, a policy at KDC side can reject the
   ticket issuance based on a lack of certain indicator

 - when a server application presented with a service ticket from a
   client, it can verify that this ticket contains intended
   authentication indicators before authorizing access from the client.

Add support to validate presence of a specific (set of) authentication
indicator(s) in pam_sss_gss when validating a user's TGT.

This concept can be used to only allow access to a PAM service when user
is in possession of a ticket obtained using some of pre-authentication
mechanisms that require multiple factors: smart-cards (PKINIT), 2FA
tokens (otp/radius), etc.

Resolves: https://github.com/SSSD/sssd/issues/5482
Signed-off-by: Alexander Bokovoy <aboko...@redhat.com>
---
 src/confdb/confdb.c               |  13 +++
 src/confdb/confdb.h               |   3 +
 src/man/pam_sss_gss.8.xml         |   6 ++
 src/man/sssd.conf.5.xml           |  44 ++++++++
 src/responder/pam/pamsrv_gssapi.c | 170 ++++++++++++++++++++++++++++++
 5 files changed, 236 insertions(+)

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index befcfff2db..4f00034044 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -1603,6 +1603,19 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
         }
     }
 
+    tmp = ldb_msg_find_attr_as_string(res->msgs[0],
+                                      CONFDB_PAM_GSSAPI_INDICATORS_MAP,
+                                      NULL);
+    if (tmp != NULL) {
+        ret = split_on_separator(domain, tmp, ',', true, true,
+                                 &domain->gssapi_indicators_map, NULL);
+        if (ret != 0) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "Cannot parse %s\n", CONFDB_PAM_GSSAPI_INDICATORS_MAP);
+            goto done;
+        }
+    }
+
     domain->has_views = false;
     domain->view_name = NULL;
 
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 036f9ecadf..a2be227ddd 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -146,6 +146,7 @@
 #define CONFDB_PAM_INITGROUPS_SCHEME "pam_initgroups_scheme"
 #define CONFDB_PAM_GSSAPI_SERVICES "pam_gssapi_services"
 #define CONFDB_PAM_GSSAPI_CHECK_UPN "pam_gssapi_check_upn"
+#define CONFDB_PAM_GSSAPI_INDICATORS_MAP "pam_gssapi_indicators_map"
 
 /* SUDO */
 #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
@@ -437,6 +438,8 @@ struct sss_domain_info {
     /* List of PAM services that are allowed to authenticate with GSSAPI. */
     char **gssapi_services;
     char *gssapi_check_upn; /* true | false | NULL */
+    /* List of indicators associated with the specific PAM service */
+    char **gssapi_indicators_map;
 };
 
 /**
diff --git a/src/man/pam_sss_gss.8.xml b/src/man/pam_sss_gss.8.xml
index ce5b11bff0..da89d27e02 100644
--- a/src/man/pam_sss_gss.8.xml
+++ b/src/man/pam_sss_gss.8.xml
@@ -70,6 +70,12 @@
                 <manvolnum>5</manvolnum>
             </citerefentry> for more details on these options.
         </para>
+        <para>
+            Some Kerberos deployments allow to assocate authentication indicators with a particular pre-authentication method used to obtain the ticket granting ticket by the user.            <command>pam_sss_gss.so</command> allows to enforce presence of authentication indicators in the service tickets before a particular PAM service can be accessed.
+        </para>
+        <para>
+            If <option>pam_gssapi_indicators_map</option> is set in the [pam] or domain section of sssd.conf, then SSSD will perform a check of the presence of any configured indicators in the service ticket.
+        </para>
     </refsect1>
 
     <refsect1 id='options'>
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 3ac1642782..f5eaa543e9 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -1771,6 +1771,50 @@ pam_gssapi_services = sudo, sudo-i
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>pam_gssapi_indicators_map</term>
+                    <listitem>
+                        <para>
+                            Comma separated list of authentication indicators required to be present in a Kerberos ticket to access a PAM service that is allowed to try GSSAPI authentication using pam_sss_gss.so module.
+                        </para>
+                        <para>
+                            Each element of the list can be either authentication indicator name or a pair
+                            <quote>service:indicator</quote>. Indicators not prefixed with the PAM service name will be required to be present to access any PAM service configured to be used with <option>pam_gssapi_services</option>.
+                        </para>
+                        <para>
+                            Note: This option can also be set per-domain which
+                            overwrites the value in [pam] section. It can also
+                            be set for trusted domain which overwrites the value
+                            in the domain section.
+                        </para>
+                        <para>
+                            Following authentication indicators are supported by IPA Kerberos deployments:
+                            <itemizedlist>
+                                <listitem>
+                                    <para>pkinit -- pre-authentication using X.509 certificates -- whether stored in files or on smart cards.</para>
+                                </listitem>
+                                <listitem>
+                                    <para>hardened -- SPAKE pre-authentication or any pre-authentication wrapped in a FAST channel.</para>
+                                </listitem>
+                                <listitem>
+                                    <para>radius -- pre-authentication with the help of a RADIUS server.</para>
+                                </listitem>
+                                <listitem>
+                                    <para>otp -- pre-authentication using integrated two-factor authentication (2FA or one-time password, OTP) in IPA.</para>
+                                </listitem>
+                            </itemizedlist>
+                        </para>
+                        <para>
+                            Example: to require access to SUDO services only for users which obtained their Kerberos tickets with a smart card-based pre-authentication (PKINIT), set
+                            <programlisting>
+pam_gssapi_indicators_map = sudo:pkinit, sudo-i:pkinit
+                            </programlisting>
+                        </para>
+                        <para>
+                            Default: not set (use of authentication indicators is not required)
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect2>
 
diff --git a/src/responder/pam/pamsrv_gssapi.c b/src/responder/pam/pamsrv_gssapi.c
index 2d05c7888a..40396f7942 100644
--- a/src/responder/pam/pamsrv_gssapi.c
+++ b/src/responder/pam/pamsrv_gssapi.c
@@ -18,12 +18,14 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <asm-generic/errno-base.h>
 #include <errno.h>
 #include <gssapi.h>
 #include <gssapi/gssapi_ext.h>
 #include <gssapi/gssapi_krb5.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 #include <talloc.h>
 #include <ldb.h>
 
@@ -83,6 +85,63 @@ static bool pam_gssapi_should_check_upn(struct pam_ctx *pam_ctx,
     return pam_ctx->gssapi_check_upn;
 }
 
+static int pam_gssapi_check_indicators(struct pam_ctx *pam_ctx,
+                                        struct sss_domain_info *domain,
+                                        const char *pam_service,
+                                        char **indicators)
+{
+    char *authind = NULL;
+    size_t pam_len = strlen(pam_service);
+    char **map = domain->gssapi_indicators_map;
+    int res;
+
+    authind = talloc_strdup(pam_ctx, "");
+    for (int i = 0; map[i]; i++) {
+        if (!strchr(map[i], ':')) {
+            authind = talloc_asprintf_append(authind, "%s ", map[i]);
+            continue;
+        }
+
+        res = strncmp(map[i], pam_service, pam_len);
+        if (res == 0) {
+            if ((strlen(map[i]) > pam_len) && (map[i][pam_len] == ':')) {
+                authind = talloc_asprintf_append(authind, "%s ",
+                                                    map[i] + (pam_len + 1));
+            } else {
+                DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: %s.",
+                        CONFDB_PAM_GSSAPI_INDICATORS_MAP, map[i]);
+                talloc_free(authind);
+                return EINVAL;
+            }
+        }
+    }
+
+    res = ENOENT;
+    if (authind != NULL) {
+        res = split_on_separator(pam_ctx, authind, ' ', true, true,
+                                &map, NULL);
+        if (res != 0) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                "Cannot parse list of indicators: [%s]\n", authind);
+            talloc_free(authind);
+            return EINVAL;
+        }
+
+        talloc_free(authind);
+        for (int i = 0; indicators[i]; i++) {
+            res = string_in_list(indicators[i], map, true);
+            if (res != 0) {
+                talloc_free(map);
+                return EOK;
+            }
+        }
+
+        res = EPERM;
+        talloc_free(map);
+    }
+    return res;
+}
+
 static bool pam_gssapi_allowed(struct pam_ctx *pam_ctx,
                                struct sss_domain_info *domain,
                                const char *service)
@@ -385,12 +444,110 @@ static char *gssapi_get_name(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
     return exported;
 }
 
+static char **gssapi_get_indicators(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
+{
+    gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+    int is_mechname;
+    OM_uint32 major;
+    OM_uint32 minor;
+    char *exported = NULL;
+    char **map = NULL;
+    int res;
+
+    major = gss_inquire_name(&minor, gss_name, &is_mechname, NULL, &attrs);
+    if (major != GSS_S_COMPLETE) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to inquire name\n");
+        return NULL;
+    }
+
+    if (attrs != GSS_C_NO_BUFFER_SET) {
+        for (int i = 0; i < attrs->count; i++) {
+            gss_buffer_desc value;
+            gss_buffer_desc display_value;
+            int authenticated = 0;
+            int complete = 0;
+            int more = -1;
+
+            /* skip anything but auth-indicators */
+            if (strcmp(attrs->elements[i].value, "auth-indicators") != 0)
+                continue;
+
+            /* retrieve all indicators */
+            while (more != 0) {
+                value.value = NULL;
+                display_value.value = NULL;
+
+                major = gss_get_name_attribute(&minor, gss_name,
+                                               &attrs->elements[i],
+                                               &authenticated,
+                                               &complete, &value,
+                                               &display_value,
+                                               &more);
+                if (major != GSS_S_COMPLETE) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Unable to retrieve an attribute\n");
+                    gss_release_buffer(&minor, &value);
+                    gss_release_buffer(&minor, &display_value);
+                    TALLOC_FREE(exported);
+                    (void)gss_release_buffer_set(&minor, &attrs);
+                    return NULL;
+                }
+
+                if (exported == NULL) {
+                    exported = talloc_strdup(mem_ctx, "");
+                    if (exported == NULL) {
+                        DEBUG(SSSDBG_CRIT_FAILURE,
+                                "Unable to pre-allocate attributes\n");
+                        (void)gss_release_buffer(&minor, &value);
+                        (void)gss_release_buffer(&minor, &display_value);
+                        (void)gss_release_buffer_set(&minor, &attrs);
+                        return NULL;
+                    }
+                }
+
+                exported = talloc_asprintf_append(exported, "%*s ",
+                                                  (int) display_value.length,
+                                                  (char*) display_value.value);
+                if (exported == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Unable to collect an attribute value\n");
+                    (void)gss_release_buffer(&minor, &value);
+                    (void)gss_release_buffer(&minor, &display_value);
+                    (void)gss_release_buffer_set(&minor, &attrs);
+                    return NULL;
+                }
+                (void)gss_release_buffer(&minor, &value);
+                (void)gss_release_buffer(&minor, &display_value);
+            }
+        }
+    }
+
+    (void)gss_release_buffer_set(&minor, &attrs);
+
+    res = split_on_separator(mem_ctx, exported, ' ', true, true,
+                            &map, NULL);
+    if (res != 0) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+            "Cannot parse list of indicators: [%s]\n", exported);
+        talloc_free(exported);
+        return NULL;
+    } else {
+        DEBUG(SSSDBG_TRACE_FUNC, "Authentication indicators: [%s]\n",
+              exported);
+    }
+
+    talloc_free(exported);
+    return map;
+}
+
+
 struct gssapi_state {
     struct cli_ctx *cli_ctx;
     struct sss_domain_info *domain;
     const char *username;
 
     char *authenticated_upn;
+    char **auth_indicators;
     bool established;
     gss_ctx_id_t ctx;
 };
@@ -568,6 +725,8 @@ gssapi_handshake(struct gssapi_state *state,
     DEBUG(SSSDBG_TRACE_FUNC, "Security context established with [%s]\n",
           state->authenticated_upn);
 
+    state->auth_indicators = gssapi_get_indicators(state, client_name);
+
     state->established = true;
     ret = EOK;
 
@@ -699,6 +858,17 @@ pam_cmd_gssapi_sec_ctx(struct cli_ctx *cli_ctx)
         goto done;
     }
 
+    if (domain->gssapi_indicators_map != NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Checking that acquired service ticket has required indicators\n");
+        ret = pam_gssapi_check_indicators(pam_ctx, domain,
+                                          pam_service,
+                                          state->auth_indicators);
+        if (ret == EPERM) {
+            goto done;
+        }
+    }
+
     if (!pam_gssapi_should_check_upn(pam_ctx, domain)) {
         /* We are done. */
         goto done;
_______________________________________________
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://docs.fedoraproject.org/en-US/project/code-of-conduct/
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