Blame SOURCES/0044-pam_sss_gss-support-authentication-indicators.patch

f55c47
From c2e8879189ecbbdfdd4b42395319a4cd91cb569f Mon Sep 17 00:00:00 2001
f55c47
From: Alexey Tikhonov <atikhono@redhat.com>
f55c47
Date: Fri, 12 Feb 2021 20:02:52 +0100
f55c47
Subject: [PATCH] pam_sss_gss: support authentication indicators (upstream
f55c47
patch 5ce7ced269c7b3dd8f75122a50f539083b5697ae by Alexander Bokovoy)
f55c47
f55c47
MIT Kerberos allows to associate authentication indicators with the
f55c47
issued ticket based on the way how the TGT was obtained. The indicators
f55c47
present in the TGT then copied to service tickets. There are two ways to
f55c47
check the authentication indicators:
f55c47
f55c47
 - when KDC issues a service ticket, a policy at KDC side can reject the
f55c47
   ticket issuance based on a lack of certain indicator
f55c47
f55c47
 - when a server application presented with a service ticket from a
f55c47
   client, it can verify that this ticket contains intended
f55c47
   authentication indicators before authorizing access from the client.
f55c47
f55c47
Add support to validate presence of a specific (set of) authentication
f55c47
indicator(s) in pam_sss_gss when validating a user's TGT.
f55c47
f55c47
This concept can be used to only allow access to a PAM service when user
f55c47
is in possession of a ticket obtained using some of pre-authentication
f55c47
mechanisms that require multiple factors: smart-cards (PKINIT), 2FA
f55c47
tokens (otp/radius), etc.
f55c47
f55c47
Patch by: Alexander Bokovoy <abokovoy@redhat.com>
f55c47
f55c47
Reviewed by: Sumit Bose <sbose@redhat.com>
f55c47
f55c47
Adapted to 8.4 branch by: Alexey Tikhonov <atikhono@redhat.com>
f55c47
---
f55c47
 src/confdb/confdb.c                  |  13 ++
f55c47
 src/confdb/confdb.h                  |   3 +
f55c47
 src/config/SSSDConfig/sssdoptions.py |   2 +
f55c47
 src/config/SSSDConfigTest.py         |   6 +-
f55c47
 src/config/cfg_rules.ini             |   3 +
f55c47
 src/config/etc/sssd.api.conf         |   2 +
f55c47
 src/db/sysdb_subdomains.c            |  12 ++
f55c47
 src/man/pam_sss_gss.8.xml            |  13 ++
f55c47
 src/man/sssd.conf.5.xml              |  64 +++++++
f55c47
 src/responder/pam/pamsrv.c           |  21 +++
f55c47
 src/responder/pam/pamsrv.h           |   2 +
f55c47
 src/responder/pam/pamsrv_gssapi.c    | 250 +++++++++++++++++++++++++++
f55c47
 12 files changed, 389 insertions(+), 2 deletions(-)
f55c47
f55c47
diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
f55c47
index befcfff..cca7615 100644
f55c47
--- a/src/confdb/confdb.c
f55c47
+++ b/src/confdb/confdb.c
f55c47
@@ -1603,6 +1603,19 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
f55c47
         }
f55c47
     }
f55c47
 
f55c47
+    tmp = ldb_msg_find_attr_as_string(res->msgs[0],
f55c47
+                                      CONFDB_PAM_GSSAPI_INDICATORS_MAP,
f55c47
+                                      NULL);
f55c47
+    if (tmp != NULL && tmp[0] != '\0') {
f55c47
+        ret = split_on_separator(domain, tmp, ',', true, true,
f55c47
+                                 &domain->gssapi_indicators_map, NULL);
f55c47
+        if (ret != 0) {
f55c47
+            DEBUG(SSSDBG_FATAL_FAILURE,
f55c47
+                  "Cannot parse %s\n", CONFDB_PAM_GSSAPI_INDICATORS_MAP);
f55c47
+            goto done;
f55c47
+        }
f55c47
+    }
f55c47
+
f55c47
     domain->has_views = false;
f55c47
     domain->view_name = NULL;
f55c47
 
f55c47
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
f55c47
index 036f9ec..a2be227 100644
f55c47
--- a/src/confdb/confdb.h
f55c47
+++ b/src/confdb/confdb.h
f55c47
@@ -146,6 +146,7 @@
f55c47
 #define CONFDB_PAM_INITGROUPS_SCHEME "pam_initgroups_scheme"
f55c47
 #define CONFDB_PAM_GSSAPI_SERVICES "pam_gssapi_services"
f55c47
 #define CONFDB_PAM_GSSAPI_CHECK_UPN "pam_gssapi_check_upn"
f55c47
+#define CONFDB_PAM_GSSAPI_INDICATORS_MAP "pam_gssapi_indicators_map"
f55c47
 
f55c47
 /* SUDO */
f55c47
 #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
f55c47
@@ -437,6 +438,8 @@ struct sss_domain_info {
f55c47
     /* List of PAM services that are allowed to authenticate with GSSAPI. */
f55c47
     char **gssapi_services;
f55c47
     char *gssapi_check_upn; /* true | false | NULL */
f55c47
+    /* List of indicators associated with the specific PAM service */
f55c47
+    char **gssapi_indicators_map;
f55c47
 };
f55c47
 
f55c47
 /**
f55c47
diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py
f55c47
index 5da52a9..0d849bc 100644
f55c47
--- a/src/config/SSSDConfig/sssdoptions.py
f55c47
+++ b/src/config/SSSDConfig/sssdoptions.py
f55c47
@@ -106,6 +106,8 @@ class SSSDOptions(object):
f55c47
         'pam_initgroups_scheme' : _('When shall the PAM responder force an initgroups request'),
f55c47
         'pam_gssapi_services' : _('List of PAM services that are allowed to authenticate with GSSAPI.'),
f55c47
         'pam_gssapi_check_upn' : _('Whether to match authenticated UPN with target user'),
f55c47
+        'pam_gssapi_indicators_map' : _('List of pairs <PAM service>:<authentication indicator> that '
f55c47
+                                        'must be enforced for PAM access with GSSAPI authentication'),
f55c47
 
f55c47
         # [sudo]
f55c47
         'sudo_timed': _('Whether to evaluate the time-based attributes in sudo rules'),
f55c47
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
f55c47
index ea4e4f6..d0422df 100755
f55c47
--- a/src/config/SSSDConfigTest.py
f55c47
+++ b/src/config/SSSDConfigTest.py
f55c47
@@ -655,7 +655,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
f55c47
             'cached_auth_timeout',
f55c47
             'auto_private_groups',
f55c47
             'pam_gssapi_services',
f55c47
-            'pam_gssapi_check_upn']
f55c47
+            'pam_gssapi_check_upn',
f55c47
+            'pam_gssapi_indicators_map']
f55c47
 
f55c47
         self.assertTrue(type(options) == dict,
f55c47
                         "Options should be a dictionary")
f55c47
@@ -1034,7 +1035,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
f55c47
             'cached_auth_timeout',
f55c47
             'auto_private_groups',
f55c47
             'pam_gssapi_services',
f55c47
-            'pam_gssapi_check_upn']
f55c47
+            'pam_gssapi_check_upn',
f55c47
+            'pam_gssapi_indicators_map']
f55c47
 
f55c47
         self.assertTrue(type(options) == dict,
f55c47
                         "Options should be a dictionary")
f55c47
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
f55c47
index 6642c63..872ceba 100644
f55c47
--- a/src/config/cfg_rules.ini
f55c47
+++ b/src/config/cfg_rules.ini
f55c47
@@ -141,6 +141,7 @@ option = p11_uri
f55c47
 option = pam_initgroups_scheme
f55c47
 option = pam_gssapi_services
f55c47
 option = pam_gssapi_check_upn
f55c47
+option = pam_gssapi_indicators_map
f55c47
 
f55c47
 [rule/allowed_sudo_options]
f55c47
 validator = ini_allowed_options
f55c47
@@ -441,6 +442,7 @@ option = re_expression
f55c47
 option = auto_private_groups
f55c47
 option = pam_gssapi_services
f55c47
 option = pam_gssapi_check_upn
f55c47
+option = pam_gssapi_indicators_map
f55c47
 
f55c47
 #Entry cache timeouts
f55c47
 option = entry_cache_user_timeout
f55c47
@@ -837,6 +839,7 @@ option = use_fully_qualified_names
f55c47
 option = auto_private_groups
f55c47
 option = pam_gssapi_services
f55c47
 option = pam_gssapi_check_upn
f55c47
+option = pam_gssapi_indicators_map
f55c47
 
f55c47
 [rule/sssd_checks]
f55c47
 validator = sssd_checks
f55c47
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
f55c47
index d3cad73..49ced63 100644
f55c47
--- a/src/config/etc/sssd.api.conf
f55c47
+++ b/src/config/etc/sssd.api.conf
f55c47
@@ -82,6 +82,7 @@ p11_uri = str, None, false
f55c47
 pam_initgroups_scheme = str, None, false
f55c47
 pam_gssapi_services = str, None, false
f55c47
 pam_gssapi_check_upn = bool, None, false
f55c47
+pam_gssapi_indicators_map = str, None, false
f55c47
 
f55c47
 [sudo]
f55c47
 # sudo service
f55c47
@@ -203,6 +204,7 @@ re_expression = str, None, false
f55c47
 auto_private_groups = str, None, false
f55c47
 pam_gssapi_services = str, None, false
f55c47
 pam_gssapi_check_upn = bool, None, false
f55c47
+pam_gssapi_indicators_map = str, None, false
f55c47
 
f55c47
 #Entry cache timeouts
f55c47
 entry_cache_user_timeout = int, None, false
f55c47
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
f55c47
index 03ba121..2243872 100644
f55c47
--- a/src/db/sysdb_subdomains.c
f55c47
+++ b/src/db/sysdb_subdomains.c
f55c47
@@ -185,6 +185,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
f55c47
     dom->override_gid = parent->override_gid;
f55c47
 
f55c47
     dom->gssapi_services = parent->gssapi_services;
f55c47
+    dom->gssapi_indicators_map = parent->gssapi_indicators_map;
f55c47
 
f55c47
     if (parent->sysdb == NULL) {
f55c47
         DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n");
f55c47
@@ -266,6 +267,17 @@ check_subdom_config_file(struct confdb_ctx *confdb,
f55c47
         goto done;
f55c47
     }
f55c47
 
f55c47
+    /* allow to set pam_gssapi_indicators_map */
f55c47
+    ret = confdb_get_string_as_list(confdb, subdomain, sd_conf_path,
f55c47
+                                    CONFDB_PAM_GSSAPI_INDICATORS_MAP,
f55c47
+                                    &subdomain->gssapi_indicators_map);
f55c47
+    if (ret != EOK && ret != ENOENT) {
f55c47
+        DEBUG(SSSDBG_OP_FAILURE,
f55c47
+              "Failed to get %s option for the subdomain: %s\n",
f55c47
+              CONFDB_PAM_GSSAPI_INDICATORS_MAP, subdomain->name);
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
     ret = EOK;
f55c47
 done:
f55c47
     talloc_free(tmp_ctx);
f55c47
diff --git a/src/man/pam_sss_gss.8.xml b/src/man/pam_sss_gss.8.xml
f55c47
index ce5b11b..a83369d 100644
f55c47
--- a/src/man/pam_sss_gss.8.xml
f55c47
+++ b/src/man/pam_sss_gss.8.xml
f55c47
@@ -70,6 +70,19 @@
f55c47
                 <manvolnum>5</manvolnum>
f55c47
             </citerefentry> for more details on these options.
f55c47
         </para>
f55c47
+        <para>
f55c47
+            Some Kerberos deployments allow to assocate authentication
f55c47
+            indicators with a particular pre-authentication method used to
f55c47
+            obtain the ticket granting ticket by the user.
f55c47
+            <command>pam_sss_gss.so</command> allows to enforce presence of
f55c47
+            authentication indicators in the service tickets before a particular
f55c47
+            PAM service can be accessed.
f55c47
+        </para>
f55c47
+        <para>
f55c47
+            If <option>pam_gssapi_indicators_map</option> is set in the [pam] or
f55c47
+            domain section of sssd.conf, then SSSD will perform a check of the
f55c47
+            presence of any configured indicators in the service ticket.
f55c47
+        </para>
f55c47
     </refsect1>
f55c47
 
f55c47
     <refsect1 id='options'>
f55c47
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
f55c47
index 8b330de..3a9955b 100644
f55c47
--- a/src/man/sssd.conf.5.xml
f55c47
+++ b/src/man/sssd.conf.5.xml
f55c47
@@ -1770,6 +1770,70 @@ pam_gssapi_services = sudo, sudo-i
f55c47
                         </para>
f55c47
                     </listitem>
f55c47
                 </varlistentry>
f55c47
+                <varlistentry>
f55c47
+                    <term>pam_gssapi_indicators_map</term>
f55c47
+                    <listitem>
f55c47
+                        <para>
f55c47
+                           Comma separated list of authentication indicators required
f55c47
+                           to be present in a Kerberos ticket to access a PAM service
f55c47
+                           that is allowed to try GSSAPI authentication using
f55c47
+                           pam_sss_gss.so module.
f55c47
+                        </para>
f55c47
+                        <para>
f55c47
+                           Each element of the list can be either an authentication indicator
f55c47
+                           name or a pair <quote>service:indicator</quote>. Indicators not
f55c47
+                           prefixed with the PAM service name will be required to access any
f55c47
+                           PAM service configured to be used with
f55c47
+                           <option>pam_gssapi_services</option>. A resulting list of indicators
f55c47
+                           per PAM service is then checked against indicators in the Kerberos
f55c47
+                           ticket during authentication by pam_sss_gss.so. Any indicator from the
f55c47
+                           ticket that matches the resulting list of indicators for the PAM service
f55c47
+                           would grant access. If none of the indicators in the list match, access
f55c47
+                           will be denied. If the resulting list of indicators for the PAM service
f55c47
+                           is empty, the check will not prevent the access.
f55c47
+                        </para>
f55c47
+                        <para>
f55c47
+                           To disable GSSAPI authentication indicator check, set this option
f55c47
+                           to <quote>-</quote> (dash). To disable the check for a specific PAM
f55c47
+                           service, add <quote>service:-</quote>.
f55c47
+                        </para>
f55c47
+                        <para>
f55c47
+                           Note: This option can also be set per-domain which
f55c47
+                           overwrites the value in [pam] section. It can also
f55c47
+                           be set for trusted domain which overwrites the value
f55c47
+                           in the domain section.
f55c47
+                        </para>
f55c47
+                        <para>
f55c47
+                            Following authentication indicators are supported by IPA Kerberos deployments:
f55c47
+                            <itemizedlist>
f55c47
+                                <listitem>
f55c47
+                                    <para>pkinit -- pre-authentication using X.509 certificates -- whether stored in files or on smart cards.</para>
f55c47
+                                </listitem>
f55c47
+                                <listitem>
f55c47
+                                    <para>hardened -- SPAKE pre-authentication or any pre-authentication wrapped in a FAST channel.</para>
f55c47
+                                </listitem>
f55c47
+                                <listitem>
f55c47
+                                    <para>radius -- pre-authentication with the help of a RADIUS server.</para>
f55c47
+                                </listitem>
f55c47
+                                <listitem>
f55c47
+                                    <para>otp -- pre-authentication using integrated two-factor authentication (2FA or one-time password, OTP) in IPA.</para>
f55c47
+                                </listitem>
f55c47
+                            </itemizedlist>
f55c47
+                        </para>
f55c47
+                        <para>
f55c47
+                            Example: to require access to SUDO services only
f55c47
+                            for users which obtained their Kerberos tickets
f55c47
+                            with a X.509 certificate pre-authentication
f55c47
+                            (PKINIT), set
f55c47
+                                <programlisting>
f55c47
+pam_gssapi_indicators_map = sudo:pkinit, sudo-i:pkinit
f55c47
+                            </programlisting>
f55c47
+                        </para>
f55c47
+                        <para>
f55c47
+                            Default: not set (use of authentication indicators is not required)
f55c47
+                        </para>
f55c47
+                    </listitem>
f55c47
+                </varlistentry>
f55c47
             </variablelist>
f55c47
         </refsect2>
f55c47
 
f55c47
diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
f55c47
index 3904c09..9b4d6c1 100644
f55c47
--- a/src/responder/pam/pamsrv.c
f55c47
+++ b/src/responder/pam/pamsrv.c
f55c47
@@ -370,6 +370,27 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
f55c47
         goto done;
f55c47
     }
f55c47
 
f55c47
+    ret = confdb_get_string(pctx->rctx->cdb, pctx, CONFDB_PAM_CONF_ENTRY,
f55c47
+                            CONFDB_PAM_GSSAPI_INDICATORS_MAP, "-", &tmpstr);
f55c47
+    if (ret != EOK) {
f55c47
+        DEBUG(SSSDBG_FATAL_FAILURE,
f55c47
+              "Failed to determine gssapi services.\n");
f55c47
+        goto done;
f55c47
+    }
f55c47
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Found value [%s] for option [%s].\n", tmpstr,
f55c47
+                                 CONFDB_PAM_GSSAPI_INDICATORS_MAP);
f55c47
+
f55c47
+    if (tmpstr != NULL) {
f55c47
+        ret = split_on_separator(pctx, tmpstr, ',', true, true,
f55c47
+                                 &pctx->gssapi_indicators_map, NULL);
f55c47
+        if (ret != EOK) {
f55c47
+            DEBUG(SSSDBG_MINOR_FAILURE,
f55c47
+                  "split_on_separator() failed [%d]: [%s].\n", ret,
f55c47
+                  sss_strerror(ret));
f55c47
+            goto done;
f55c47
+        }
f55c47
+    }
f55c47
+
f55c47
     /* The responder is initialized. Now tell it to the monitor. */
f55c47
     ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_PAM,
f55c47
                                    SSS_PAM_SBUS_SERVICE_NAME,
f55c47
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
f55c47
index 3553296..383c7be 100644
f55c47
--- a/src/responder/pam/pamsrv.h
f55c47
+++ b/src/responder/pam/pamsrv.h
f55c47
@@ -65,6 +65,8 @@ struct pam_ctx {
f55c47
 
f55c47
     /* List of PAM services that are allowed to authenticate with GSSAPI. */
f55c47
     char **gssapi_services;
f55c47
+    /* List of authentication indicators associated with a PAM service */
f55c47
+    char **gssapi_indicators_map;
f55c47
     bool gssapi_check_upn;
f55c47
 };
f55c47
 
f55c47
diff --git a/src/responder/pam/pamsrv_gssapi.c b/src/responder/pam/pamsrv_gssapi.c
f55c47
index 2d05c78..e4da4c4 100644
f55c47
--- a/src/responder/pam/pamsrv_gssapi.c
f55c47
+++ b/src/responder/pam/pamsrv_gssapi.c
f55c47
@@ -24,6 +24,7 @@
f55c47
 #include <gssapi/gssapi_krb5.h>
f55c47
 #include <stdint.h>
f55c47
 #include <stdlib.h>
f55c47
+#include <string.h>
f55c47
 #include <talloc.h>
f55c47
 #include <ldb.h>
f55c47
 
f55c47
@@ -83,6 +84,117 @@ static bool pam_gssapi_should_check_upn(struct pam_ctx *pam_ctx,
f55c47
     return pam_ctx->gssapi_check_upn;
f55c47
 }
f55c47
 
f55c47
+static int pam_gssapi_check_indicators(TALLOC_CTX *mem_ctx,
f55c47
+                                       const char *pam_service,
f55c47
+                                       char **gssapi_indicators_map,
f55c47
+                                       char **indicators)
f55c47
+{
f55c47
+    char *authind = NULL;
f55c47
+    size_t pam_len = strlen(pam_service);
f55c47
+    char **map = gssapi_indicators_map;
f55c47
+    char **result = NULL;
f55c47
+    int res;
f55c47
+
f55c47
+    authind = talloc_strdup(mem_ctx, "");
f55c47
+    if (authind == NULL) {
f55c47
+        return ENOMEM;
f55c47
+    }
f55c47
+
f55c47
+    for (int i = 0; map[i]; i++) {
f55c47
+        if (map[i][0] == '-') {
f55c47
+            DEBUG(SSSDBG_TRACE_FUNC,
f55c47
+                  "Indicators aren't used for [%s]\n",
f55c47
+                  pam_service);
f55c47
+            talloc_free(authind);
f55c47
+            return EOK;
f55c47
+        }
f55c47
+        if (!strchr(map[i], ':')) {
f55c47
+            authind = talloc_asprintf_append(authind, "%s ", map[i]);
f55c47
+            if (authind == NULL) {
f55c47
+                /* Since we allocate on pam_ctx, caller will free it */
f55c47
+                return ENOMEM;
f55c47
+            }
f55c47
+            continue;
f55c47
+        }
f55c47
+
f55c47
+        res = strncmp(map[i], pam_service, pam_len);
f55c47
+        if (res == 0) {
f55c47
+            if (strlen(map[i]) > pam_len) {
f55c47
+                if (map[i][pam_len] != ':') {
f55c47
+                    /* different PAM service, skip it */
f55c47
+                    continue;
f55c47
+                }
f55c47
+
f55c47
+                if (map[i][pam_len + 1] == '-') {
f55c47
+                    DEBUG(SSSDBG_TRACE_FUNC,
f55c47
+                        "Indicators aren't used for [%s]\n",
f55c47
+                        pam_service);
f55c47
+                    talloc_free(authind);
f55c47
+                    return EOK;
f55c47
+                }
f55c47
+
f55c47
+                authind = talloc_asprintf_append(authind, "%s ",
f55c47
+                                                 map[i] + (pam_len + 1));
f55c47
+                if (authind == NULL) {
f55c47
+                    /* Since we allocate on pam_ctx, caller will free it */
f55c47
+                    return ENOMEM;
f55c47
+                }
f55c47
+            } else {
f55c47
+                DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: [%s]\n",
f55c47
+                      CONFDB_PAM_GSSAPI_INDICATORS_MAP, map[i]);
f55c47
+                talloc_free(authind);
f55c47
+                return EINVAL;
f55c47
+            }
f55c47
+        }
f55c47
+    }
f55c47
+
f55c47
+    res = ENOENT;
f55c47
+    map = NULL;
f55c47
+
f55c47
+    if (authind[0] == '\0') {
f55c47
+        /* empty list of per-service indicators -> skip */
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
+    /* trim a space after the final indicator
f55c47
+     * to prevent split_on_separator() to fail */
f55c47
+    authind[strlen(authind) - 1] = '\0';
f55c47
+
f55c47
+    res = split_on_separator(mem_ctx, authind, ' ', true, true,
f55c47
+                             &map, NULL);
f55c47
+    if (res != 0) {
f55c47
+        DEBUG(SSSDBG_FATAL_FAILURE,
f55c47
+            "Cannot parse list of indicators: [%s]\n", authind);
f55c47
+        res = EINVAL;
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
+    res = diff_string_lists(mem_ctx, indicators, map, NULL, NULL, &result);
f55c47
+    if (res != 0) {
f55c47
+        DEBUG(SSSDBG_FATAL_FAILURE,"Cannot diff lists of indicators\n");
f55c47
+        res = EINVAL;
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
+    if (result && result[0] != NULL) {
f55c47
+        for (int i = 0; result[i]; i++) {
f55c47
+            DEBUG(SSSDBG_TRACE_FUNC,
f55c47
+                  "indicator [%s] is allowed for PAM service [%s]\n",
f55c47
+                  result[i], pam_service);
f55c47
+        }
f55c47
+        res = EOK;
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
+    res = EPERM;
f55c47
+
f55c47
+done:
f55c47
+    talloc_free(result);
f55c47
+    talloc_free(authind);
f55c47
+    talloc_free(map);
f55c47
+    return res;
f55c47
+}
f55c47
+
f55c47
 static bool pam_gssapi_allowed(struct pam_ctx *pam_ctx,
f55c47
                                struct sss_domain_info *domain,
f55c47
                                const char *service)
f55c47
@@ -385,12 +497,126 @@ static char *gssapi_get_name(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
f55c47
     return exported;
f55c47
 }
f55c47
 
f55c47
+#define AUTH_INDICATORS_TAG "auth-indicators"
f55c47
+
f55c47
+static char **gssapi_get_indicators(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
f55c47
+{
f55c47
+    gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
f55c47
+    int is_mechname;
f55c47
+    OM_uint32 major;
f55c47
+    OM_uint32 minor;
f55c47
+    gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
f55c47
+    gss_buffer_desc display_value = GSS_C_EMPTY_BUFFER;
f55c47
+    char *exported = NULL;
f55c47
+    char **map = NULL;
f55c47
+    int res;
f55c47
+
f55c47
+    major = gss_inquire_name(&minor, gss_name, &is_mechname, NULL, &attrs);
f55c47
+    if (major != GSS_S_COMPLETE) {
f55c47
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to inquire name\n");
f55c47
+        return NULL;
f55c47
+    }
f55c47
+
f55c47
+    if (attrs == GSS_C_NO_BUFFER_SET) {
f55c47
+        DEBUG(SSSDBG_TRACE_FUNC, "No krb5 attributes in the ticket\n");
f55c47
+        return NULL;
f55c47
+    }
f55c47
+
f55c47
+    exported = talloc_strdup(mem_ctx, "");
f55c47
+    if (exported == NULL) {
f55c47
+        DEBUG(SSSDBG_CRIT_FAILURE,
f55c47
+              "Unable to pre-allocate indicators\n");
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
+    for (int i = 0; i < attrs->count; i++) {
f55c47
+        int authenticated = 0;
f55c47
+        int complete = 0;
f55c47
+        int more = -1;
f55c47
+
f55c47
+        /* skip anything but auth-indicators */
f55c47
+        if (strncmp(AUTH_INDICATORS_TAG, attrs->elements[i].value,
f55c47
+                    sizeof(AUTH_INDICATORS_TAG) - 1) != 0)
f55c47
+            continue;
f55c47
+
f55c47
+        /* retrieve all indicators */
f55c47
+        while (more != 0) {
f55c47
+            value.value = NULL;
f55c47
+            display_value.value = NULL;
f55c47
+
f55c47
+            major = gss_get_name_attribute(&minor, gss_name,
f55c47
+                                            &attrs->elements[i],
f55c47
+                                            &authenticated,
f55c47
+                                            &complete, &value,
f55c47
+                                            &display_value,
f55c47
+                                            &more);
f55c47
+            if (major != GSS_S_COMPLETE) {
f55c47
+                DEBUG(SSSDBG_CRIT_FAILURE,
f55c47
+                        "Unable to retrieve an attribute\n");
f55c47
+                goto done;
f55c47
+            }
f55c47
+
f55c47
+            if ((value.value != NULL) && authenticated) {
f55c47
+                DEBUG(SSSDBG_TRACE_FUNC,
f55c47
+                        "attribute's [%.*s] value [%.*s] authenticated\n",
f55c47
+                        (int) attrs->elements[i].length,
f55c47
+                        (char*) attrs->elements[i].value,
f55c47
+                        (int) value.length,
f55c47
+                        (char*) value.value);
f55c47
+                exported = talloc_asprintf_append(exported, "%.*s ",
f55c47
+                                                (int) value.length,
f55c47
+                                                (char*) value.value);
f55c47
+            }
f55c47
+
f55c47
+            if (exported == NULL) {
f55c47
+                /* Since we allocate on mem_ctx, caller will free
f55c47
+                 * the previous version of 'exported' */
f55c47
+                DEBUG(SSSDBG_CRIT_FAILURE,
f55c47
+                        "Unable to collect an attribute value\n");
f55c47
+                goto done;
f55c47
+            }
f55c47
+            (void) gss_release_buffer(&minor, &value);
f55c47
+            (void) gss_release_buffer(&minor, &display_value);
f55c47
+        }
f55c47
+    }
f55c47
+
f55c47
+    if (exported[0] != '\0') {
f55c47
+        /* trim a space after the final indicator
f55c47
+         * to prevent split_on_separator() to fail */
f55c47
+        exported[strlen(exported) - 1] = '\0';
f55c47
+    } else {
f55c47
+        /* empty list */
f55c47
+        goto done;
f55c47
+    }
f55c47
+
f55c47
+    res = split_on_separator(mem_ctx, exported, ' ', true, true,
f55c47
+                            &map, NULL);
f55c47
+    if (res != 0) {
f55c47
+        DEBUG(SSSDBG_FATAL_FAILURE,
f55c47
+            "Cannot parse list of indicators: [%s]\n", exported);
f55c47
+        goto done;
f55c47
+    } else {
f55c47
+        DEBUG(SSSDBG_TRACE_FUNC, "authentication indicators: [%s]\n",
f55c47
+              exported);
f55c47
+    }
f55c47
+
f55c47
+done:
f55c47
+    (void) gss_release_buffer(&minor, &value);
f55c47
+    (void) gss_release_buffer(&minor, &display_value);
f55c47
+    (void) gss_release_buffer_set(&minor, &attrs);
f55c47
+
f55c47
+    talloc_free(exported);
f55c47
+    return map;
f55c47
+}
f55c47
+
f55c47
+
f55c47
 struct gssapi_state {
f55c47
     struct cli_ctx *cli_ctx;
f55c47
     struct sss_domain_info *domain;
f55c47
     const char *username;
f55c47
 
f55c47
     char *authenticated_upn;
f55c47
+    char **auth_indicators;
f55c47
     bool established;
f55c47
     gss_ctx_id_t ctx;
f55c47
 };
f55c47
@@ -568,6 +794,8 @@ gssapi_handshake(struct gssapi_state *state,
f55c47
     DEBUG(SSSDBG_TRACE_FUNC, "Security context established with [%s]\n",
f55c47
           state->authenticated_upn);
f55c47
 
f55c47
+    state->auth_indicators = gssapi_get_indicators(state, client_name);
f55c47
+
f55c47
     state->established = true;
f55c47
     ret = EOK;
f55c47
 
f55c47
@@ -632,6 +860,7 @@ pam_cmd_gssapi_sec_ctx(struct cli_ctx *cli_ctx)
f55c47
     const char *domain_name;
f55c47
     const char *username;
f55c47
     char *target;
f55c47
+    char **indicators_map = NULL;
f55c47
     size_t gss_data_len;
f55c47
     uint8_t *gss_data;
f55c47
     errno_t ret;
f55c47
@@ -699,6 +928,27 @@ pam_cmd_gssapi_sec_ctx(struct cli_ctx *cli_ctx)
f55c47
         goto done;
f55c47
     }
f55c47
 
f55c47
+    /* Use map for auth-indicators from the domain, if defined and
f55c47
+     * fallback to the [pam] section otherwise */
f55c47
+    indicators_map = domain->gssapi_indicators_map ?
f55c47
+                     domain->gssapi_indicators_map :
f55c47
+                     (pam_ctx->gssapi_indicators_map ?
f55c47
+                      pam_ctx->gssapi_indicators_map : NULL);
f55c47
+    if (indicators_map != NULL) {
f55c47
+        ret = pam_gssapi_check_indicators(state,
f55c47
+                                          pam_service,
f55c47
+                                          indicators_map,
f55c47
+                                          state->auth_indicators);
f55c47
+        DEBUG(SSSDBG_TRACE_FUNC,
f55c47
+              "Check if acquired service ticket has req. indicators: %d\n",
f55c47
+              ret);
f55c47
+        if ((ret == EPERM) || (ret == ENOMEM) || (ret == EINVAL)) {
f55c47
+            /* skip further checks if denied or no memory,
f55c47
+             * ENOENT means the check is not applicable */
f55c47
+            goto done;
f55c47
+        }
f55c47
+    }
f55c47
+
f55c47
     if (!pam_gssapi_should_check_upn(pam_ctx, domain)) {
f55c47
         /* We are done. */
f55c47
         goto done;
f55c47
-- 
f55c47
2.21.3
f55c47