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

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