>From e824d4aa1051bf405243deb06cef1e05ed3ffd25 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Fri, 27 Feb 2015 11:37:50 -0500
Subject: [PATCH 1/3] krb5: new option krb5_map_user

New option `krb5_map_user` providing mapping of ID provider names to
Kerberos principals.

Resolves:
https://fedorahosted.org/sssd/ticket/2509
---
 src/config/SSSDConfig/__init__.py.in     |   1 +
 src/config/SSSDConfigTest.py             |   9 ++-
 src/config/etc/sssd.api.d/sssd-ad.conf   |   1 +
 src/config/etc/sssd.api.d/sssd-ipa.conf  |   1 +
 src/config/etc/sssd.api.d/sssd-krb5.conf |   1 +
 src/man/sssd-krb5.5.xml                  |  26 ++++++++
 src/providers/ad/ad_opts.h               |   1 +
 src/providers/ipa/ipa_opts.h             |   1 +
 src/providers/krb5/krb5_access.c         |   4 +-
 src/providers/krb5/krb5_auth.c           |  40 +++++++++++-
 src/providers/krb5/krb5_auth.h           |   2 +
 src/providers/krb5/krb5_common.h         |   9 +++
 src/providers/krb5/krb5_init_shared.c    | 101 +++++++++++++++++++++++++++++++
 src/providers/krb5/krb5_opts.h           |   1 +
 src/util/util.c                          |  56 +++++++++++++++++
 src/util/util.h                          |   6 ++
 16 files changed, 254 insertions(+), 6 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index dbbffebf38977e526cf2944510a2f60da7edf33a..1d4b9c2cc6e20ea82222931551e0cb7ca859600e 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -216,6 +216,7 @@ option_strings = {
     'krb5_fast_principal' : _("Selects the principal to use for FAST"),
     'krb5_canonicalize' : _("Enables principal canonicalization"),
     'krb5_use_enterprise_principal' : _("Enables enterprise principals"),
+    'krb5_map_user' : _('A mapping from user names to kerberos principal names'),
 
     # [provider/krb5/chpass]
     'krb5_kpasswd' : _('Server where the change password service is running if not on the KDC'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 3a5312ea945b5247c69e97b73565b7061e037b69..87ee2b5f7d7bc81bd620a10f7dd86701b3efd870 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -621,7 +621,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_fast_principal',
              'krb5_canonicalize',
              'krb5_use_enterprise_principal',
-             'krb5_use_kdcinfo'])
+             'krb5_use_kdcinfo',
+             'krb5_map_user'])
 
         options = domain.list_options()
 
@@ -781,7 +782,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_fast_principal',
             'krb5_canonicalize',
             'krb5_use_enterprise_principal',
-            'krb5_use_kdcinfo']
+            'krb5_use_kdcinfo',
+            'krb5_map_user']
 
         self.assertTrue(type(options) == dict,
                         "Options should be a dictionary")
@@ -981,7 +983,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_fast_principal',
              'krb5_canonicalize',
              'krb5_use_enterprise_principal',
-             'krb5_use_kdcinfo'])
+             'krb5_use_kdcinfo',
+             'krb5_map_user'])
 
         options = domain.list_options()
 
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
index 5a5ea0c36b07d7497c1caa4208c7270d6de6dcc9..c46f3a7cb50db519d113e15f425c7f746d34ad81 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -139,6 +139,7 @@ krb5_renew_interval = str, None, false
 krb5_use_fast = str, None, false
 krb5_fast_principal = str, None, false
 krb5_use_enterprise_principal = bool, None, false
+krb5_map_user = str, None, false
 
 [provider/ad/access]
 
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index 2a3b7ef1519e3476cb4b432336da0c359b1844ba..d9ab673f25831da3a513da68a7113a525691a2db 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -155,6 +155,7 @@ krb5_renew_interval = str, None, false
 krb5_use_fast = str, None, false
 krb5_fast_principal = str, None, false
 krb5_use_enterprise_principal = bool, None, false
+krb5_map_user = str, None, false
 
 [provider/ipa/access]
 ipa_hbac_refresh = int, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-krb5.conf b/src/config/etc/sssd.api.d/sssd-krb5.conf
index e65ed01b688078aff090ff53b91779595fd6f465..b7423b74f7b6845d235cc523a8e249d6a74d69ab 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -21,6 +21,7 @@ krb5_use_fast = str, None, false
 krb5_fast_principal = str, None, false
 krb5_canonicalize = bool, None, false
 krb5_use_enterprise_principal = bool, None, false
+krb5_map_user = str, None, false
 
 [provider/krb5/access]
 
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 5bcab21111670a22e6d0c3cdc9eccf765e950b07..cf40ab4c70beded443933b820a9b65f7f57d1e2c 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -507,6 +507,32 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_map_user (string)</term>
+                    <listitem>
+                        <para>
+                             The list of mappings is given as a comma-separated
+                             list of pairs `username:principal` where `username`
+                             is a UNIX user name and `principal` is a kerberos
+                             principal name. This mapping is used when user is
+                             authenticating using `auth_provider = krb5`.
+                        </para>
+
+                        <para>
+                            example:
+                            krb5_map_user = joe:juser,vince:rraines
+                        </para>
+                        <para>
+                            `joe` and `vince` are UNIX user names and `juser`
+                            and `rraines` are kerberos principal names.
+                        </para>
+
+                        <para>
+                            Default: not set
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
index f4c1c523bdc57a824105dfd781eb90a88e068908..4c287021c2d901999602621a4fe3b7af9c6b8c35 100644
--- a/src/providers/ad/ad_opts.h
+++ b/src/providers/ad/ad_opts.h
@@ -168,6 +168,7 @@ struct dp_option ad_def_krb5_opts[] = {
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 66af648583e552d7edd932f6bb5a2c3bef107e51..5ce47c31ed0bb947dd1f3f361c5052a1d76fb692 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -311,6 +311,7 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/krb5/krb5_access.c b/src/providers/krb5/krb5_access.c
index 7fda2a37922a537f7fe53d629c4e0cb4df1bd4da..72b93180f9cdeb53ad78c821ed73281d38cda920 100644
--- a/src/providers/krb5/krb5_access.c
+++ b/src/providers/krb5/krb5_access.c
@@ -106,8 +106,8 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
         break;
     case 1:
         ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx,
-                                be_ctx->domain, pd->user, pd->domain,
-                                &state->kr->upn);
+                                be_ctx->domain, state->kr->mapped_name,
+                                pd->domain, &state->kr->upn);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, "find_or_guess_upn failed.\n");
             goto done;
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 25caf7b788a3f373f47e9d8aad38a2ea6fc12621..f665fab786b27bf1e85d238cdb2567915d0ae0b8 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -175,10 +175,33 @@ static int krb5_cleanup(void *ptr)
     return EOK;
 }
 
+errno_t get_krb_princ(struct map_id_name_to_krb_princ *name_to_princ, int size,
+                      char *id_prov_name, const char **_krb_princ)
+{
+    errno_t ret;
+    int i;
+
+    for (i=0; i < size; i++) {
+        if (strcmp(name_to_princ[i].id_name, id_prov_name) == 0) {
+            *_krb_princ = name_to_princ[i].krb_princ;
+            ret = EOK;
+            goto done;
+        }
+    }
+
+    /* Handle also the case of size being zero */
+    ret = ENOENT;
+
+done:
+    return ret;
+}
+
 errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
                    struct krb5_ctx *krb5_ctx, struct krb5child_req **krb5_req)
 {
     struct krb5child_req *kr = NULL;
+    const char *mapped_name;
+    errno_t ret;
 
     kr = talloc_zero(mem_ctx, struct krb5child_req);
     if (kr == NULL) {
@@ -192,6 +215,20 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
     kr->pd = pd;
     kr->krb5_ctx = krb5_ctx;
 
+    ret = get_krb_princ(krb5_ctx->name_to_princ, krb5_ctx->name_to_princ_size,
+                        pd->user, &mapped_name);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Setting mapped name to: %s\n", mapped_name);
+        kr->mapped_name = mapped_name;
+    } else if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No mapping for: %s\n", pd->user);
+        kr->mapped_name = pd->user;
+    } else {
+        DEBUG(SSSDBG_MINOR_FAILURE, "get_krb_princ failed - %s:[%d]\n",
+              sss_strerror(ret), ret);
+        kr->mapped_name = pd->user;
+    }
+
     *krb5_req = kr;
 
     return EOK;
@@ -350,6 +387,7 @@ struct krb5_auth_state {
 static void krb5_auth_resolve_done(struct tevent_req *subreq);
 static void krb5_auth_done(struct tevent_req *subreq);
 
+
 struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                                   struct tevent_context *ev,
                                   struct be_ctx *be_ctx,
@@ -510,7 +548,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
 
     case 1:
         ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx,
-                                be_ctx->domain, pd->user, pd->domain,
+                                be_ctx->domain, kr->mapped_name, pd->domain,
                                 &kr->upn);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, "find_or_guess_upn failed.\n");
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 00cb658c418ade2e6a1d9b5362a40f97e0dc6c6b..354c74f5d806c9bd9355a9abdc2976f1fcf8b1aa 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -56,6 +56,8 @@ struct krb5child_req {
     bool run_as_user;
     bool upn_from_different_realm;
     bool send_pac;
+
+    const char *mapped_name;
 };
 
 errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 81e64688a6d93a4b3ecf41d75d1bf6166b4619ce..99ee3b4930fb275efebc09e510cd0459d11bf19e 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -67,6 +67,7 @@ enum krb5_opts {
     KRB5_CANONICALIZE,
     KRB5_USE_ENTERPRISE_PRINCIPAL,
     KRB5_USE_KDCINFO,
+    KRB5_MAP_USER,
 
     KRB5_OPTS
 };
@@ -89,6 +90,11 @@ enum krb5_config_type {
     K5C_IPA_SERVER
 };
 
+struct map_id_name_to_krb_princ {
+    char *id_name;
+    char* krb_princ;
+};
+
 struct krb5_ctx {
     /* opts taken from kinit */
     /* in seconds */
@@ -128,6 +134,9 @@ struct krb5_ctx {
     hash_table_t *wait_queue_hash;
 
     enum krb5_config_type config_type;
+
+    struct map_id_name_to_krb_princ *name_to_princ;
+    int name_to_princ_size;
 };
 
 struct remove_info_files_ctx {
diff --git a/src/providers/krb5/krb5_init_shared.c b/src/providers/krb5/krb5_init_shared.c
index 3b4bf096ef49d0f2d369fda20462b7fd56d61127..e0c010fefe79ffa594a63a88c43279aecd4559c2 100644
--- a/src/providers/krb5/krb5_init_shared.c
+++ b/src/providers/krb5/krb5_init_shared.c
@@ -26,6 +26,98 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_init_shared.h"
 
+static errno_t
+fill_name_to_princ_map(TALLOC_CTX *mem_ctx, char **map,
+                       struct map_id_name_to_krb_princ *name_to_princ,
+                       int size)
+{
+    int i;
+    errno_t ret;
+
+    for (i=0; i < size; i++) {
+        ret = split_str(mem_ctx, ':', map[i],
+                        &name_to_princ[i].id_name,
+                        &name_to_princ[i].krb_princ);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "split_str failed - %s:[%d]\n", sss_strerror(ret), ret);
+            goto done;
+        } else if (name_to_princ[i].id_name == NULL ||
+                   name_to_princ[i].krb_princ == NULL) {
+            DEBUG(SSSDBG_MINOR_FAILURE, "split_str failed - %s:%s\n",
+                  name_to_princ[i].id_name, name_to_princ[i].krb_princ );
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    return ret;
+}
+
+errno_t parse_krb5_map_user(TALLOC_CTX *mem_ctx, struct dp_option *opts,
+                            struct map_id_name_to_krb_princ **_name_to_princ,
+                            int *_size)
+{
+    int size;
+    char **map;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+    const char *krb5_map_user;
+    struct map_id_name_to_krb_princ *name_to_princ;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    krb5_map_user = dp_opt_get_cstring(opts, KRB5_MAP_USER);
+    if (krb5_map_user == NULL) {
+        DEBUG(SSSDBG_FUNC_DATA, "Option krb5_map_user is empty!\n");
+        size = 0;
+        ret = EOK;
+        goto done;
+    }
+
+    ret = split_on_separator(tmp_ctx, krb5_map_user, ',', true, true,
+                             &map, &size);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to parse krb5_map_user!\n");
+        goto done;
+    }
+
+    name_to_princ = talloc_array(tmp_ctx,
+                                 struct map_id_name_to_krb_princ, size);
+    if (name_to_princ == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = fill_name_to_princ_map(name_to_princ, map, name_to_princ, size);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "fill_name_to_princ_map failed: %s:[%d]\n",
+              sss_strerror(ret), ret);
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        if (size == 0) {
+            *_name_to_princ = NULL;
+        } else {
+            *_name_to_princ = talloc_steal(mem_ctx, name_to_princ);
+        }
+        *_size = size;
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx,
                         struct be_ctx *bectx)
 {
@@ -90,6 +182,15 @@ errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx,
         goto done;
     }
 
+    ret = parse_krb5_map_user(krb5_auth_ctx, krb5_auth_ctx->opts,
+                              &krb5_auth_ctx->name_to_princ,
+                              &krb5_auth_ctx->name_to_princ_size);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "parse_krb5_map_user failed: %s:[%d]\n",
+              sss_strerror(ret), ret);
+        goto done;
+    }
+
     ret = EOK;
 
 done:
diff --git a/src/providers/krb5/krb5_opts.h b/src/providers/krb5/krb5_opts.h
index db62cc3b23d8a5eadd3df97a102fb84128b740da..50d701b8ba431719dbf67799230ec345cdee30a9 100644
--- a/src/providers/krb5/krb5_opts.h
+++ b/src/providers/krb5/krb5_opts.h
@@ -45,6 +45,7 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/util/util.c b/src/util/util.c
index 613c559bb2002686c7833642d0946e46e5a9b5d6..a3da6c51d2b92a529b03632a26b6e07569b5da77 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -30,6 +30,62 @@
 #include "util/util.h"
 #include "util/sss_utf8.h"
 
+errno_t split_str(TALLOC_CTX *mem_ctx,
+                  const char delimiter,
+                  char *str,
+                  char **_first,
+                  char **_second)
+{
+    char *sep;
+    errno_t ret;
+    char *first;
+    char *second;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    sep = strchr(str, delimiter);
+    if (sep == NULL) {
+        ret = ENOENT;
+        goto done;
+    } else {
+        if (sep == str) {
+            first = NULL;
+        } else {
+            first = talloc_strndup(tmp_ctx, str, sep - str);
+            if (first == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+        }
+
+        if (*(sep + 1) == '\0') {
+            second = NULL;
+        } else {
+            second = talloc_strdup(tmp_ctx, sep+1);
+            if (second == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        *_first = talloc_steal(mem_ctx, first);
+        *_second = talloc_steal(mem_ctx, second);
+    }
+
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
                        const char sep, bool trim, bool skip_empty,
                        char ***_list, int *size)
diff --git a/src/util/util.h b/src/util/util.h
index 3c5ce295ec4ad2ad3c3a7ae0e7482c232db79746..479e24cabe947419cf9d63e803b1b662359ad38b 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -408,6 +408,12 @@ const char * const * get_known_services(void);
 
 errno_t sss_user_by_name_or_uid(const char *input, uid_t *_uid, gid_t *_gid);
 
+errno_t split_str(TALLOC_CTX *mem_ctx,
+                  const char delimiter,
+                  char *str,
+                  char **_first,
+                  char **_second);
+
 int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
                        const char sep, bool trim, bool skip_empty,
                        char ***_list, int *size);
-- 
2.1.0

