From aa8a8318aaa3270e9d9957d0c22dec6342360a37 Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: May 28 2015 09:04:28 +0000 Subject: 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 Reviewed-by: Jakub Hrozek --- diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in index 31c9c64..f58c52f 100644 --- a/src/config/SSSDConfig/__init__.py.in +++ b/src/config/SSSDConfig/__init__.py.in @@ -215,6 +215,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 ab9d512..60f51ca 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -629,7 +629,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() @@ -789,7 +790,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") @@ -990,7 +992,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 5a5ea0c..c46f3a7 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 230bdd7..6bae609 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 e65ed01..b7423b7 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 8d5bbee..e7fdd19 100644 --- a/src/man/sssd-krb5.5.xml +++ b/src/man/sssd-krb5.5.xml @@ -516,6 +516,42 @@ + + krb5_map_user (string) + + + The list of mappings is given as a comma-separated + list of pairs username:primary + where username is a UNIX user name + and primary is a user part of + a kerberos principal. This mapping is used when + user is authenticating using + auth_provider = krb5. + + + + example: + +krb5_realm = REALM +krb5_map_user = joe:juser,dick:richard + + + + joe and dick are + UNIX user names and juser and + richard are primaries of kerberos + principals. For user joe resp. + dick SSSD will try to kinit as + juser@REALM resp. + richard@REALM. + + + + Default: not set + + + + diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h index 15b1404..6e85944 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 8a07642..34e9e16 100644 --- a/src/providers/ipa/ipa_opts.h +++ b/src/providers/ipa/ipa_opts.h @@ -310,6 +310,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 7fda2a3..3afb901 100644 --- a/src/providers/krb5/krb5_access.c +++ b/src/providers/krb5/krb5_access.c @@ -64,7 +64,8 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx, state->krb5_ctx = krb5_ctx; state->access_allowed = false; - ret = krb5_setup(state, pd, krb5_ctx, &state->kr); + ret = krb5_setup(state, pd, krb5_ctx, be_ctx->domain->case_sensitive, + &state->kr); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_setup failed.\n"); goto done; @@ -105,9 +106,8 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx, goto done; break; case 1: - ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx, - be_ctx->domain, pd->user, pd->domain, - &state->kr->upn); + ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx, be_ctx->domain, + state->kr->user, 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 148b08f..8c85144 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -36,6 +36,7 @@ #include "util/find_uid.h" #include "util/auth_utils.h" #include "db/sysdb.h" +#include "util/sss_utf8.h" #include "util/child_common.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" @@ -175,15 +176,51 @@ static int krb5_cleanup(void *ptr) return EOK; } +static errno_t +get_krb_primary(struct map_id_name_to_krb_primary *name_to_primary, + char *id_prov_name, bool cs, const char **_krb_primary) +{ + errno_t ret; + int i = 0; + + while(name_to_primary != NULL && + name_to_primary[i].id_name != NULL && + name_to_primary[i].krb_primary != NULL) { + + if (sss_string_equal(cs, name_to_primary[i].id_name, id_prov_name)) { + *_krb_primary = name_to_primary[i].krb_primary; + ret = EOK; + goto done; + } + i++; + } + + /* Handle also the case of name_to_primary being NULL */ + 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 krb5_ctx *krb5_ctx, bool cs, + struct krb5child_req **_krb5_req) { - struct krb5child_req *kr = NULL; + struct krb5child_req *kr; + const char *mapped_name; + TALLOC_CTX *tmp_ctx; + errno_t ret; - kr = talloc_zero(mem_ctx, struct krb5child_req); + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + kr = talloc_zero(tmp_ctx, struct krb5child_req); if (kr == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n"); - return ENOMEM; + ret = ENOMEM; + goto done; } kr->is_offline = false; talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup); @@ -191,9 +228,28 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd, kr->pd = pd; kr->krb5_ctx = krb5_ctx; - *krb5_req = kr; + ret = get_krb_primary(krb5_ctx->name_to_primary, + pd->user, cs, &mapped_name); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Setting mapped name to: %s\n", mapped_name); + kr->user = mapped_name; + } else if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_ALL, "No mapping for: %s\n", pd->user); + kr->user = pd->user; + } else { + DEBUG(SSSDBG_CRIT_FAILURE, "get_krb_primary failed - %s:[%d]\n", + sss_strerror(ret), ret); + goto done; + } - return EOK; + ret = EOK; + +done: + if (ret == EOK) { + *_krb5_req = talloc_steal(mem_ctx, kr); + } + talloc_free(tmp_ctx); + return ret; } @@ -502,7 +558,8 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, attrs[6] = SYSDB_AUTH_TYPE; attrs[7] = NULL; - ret = krb5_setup(state, pd, krb5_ctx, &state->kr); + ret = krb5_setup(state, pd, krb5_ctx, be_ctx->domain->case_sensitive, + &state->kr); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_setup failed.\n"); goto done; @@ -535,9 +592,8 @@ struct tevent_req *krb5_auth_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, - &kr->upn); + ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx, be_ctx->domain, + kr->user, pd->domain, &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.h b/src/providers/krb5/krb5_auth.h index c932529..a00f1d9 100644 --- a/src/providers/krb5/krb5_auth.h +++ b/src/providers/krb5/krb5_auth.h @@ -55,10 +55,13 @@ struct krb5child_req { bool valid_tgt; bool upn_from_different_realm; bool send_pac; + + const char *user; }; errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd, - struct krb5_ctx *krb5_ctx, struct krb5child_req **krb5_req); + struct krb5_ctx *krb5_ctx, bool case_sensitive, + struct krb5child_req **krb5_req); void krb5_pam_handler(struct be_req *be_req); void krb5_pam_handler_auth_done(struct tevent_req *req); diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 81e6468..8f6fb22 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_primary { + const char *id_name; + const char* krb_primary; +}; + struct krb5_ctx { /* opts taken from kinit */ /* in seconds */ @@ -128,6 +134,8 @@ struct krb5_ctx { hash_table_t *wait_queue_hash; enum krb5_config_type config_type; + + struct map_id_name_to_krb_primary *name_to_primary; }; struct remove_info_files_ctx { diff --git a/src/providers/krb5/krb5_init_shared.c b/src/providers/krb5/krb5_init_shared.c index 3b4bf09..767291c 100644 --- a/src/providers/krb5/krb5_init_shared.c +++ b/src/providers/krb5/krb5_init_shared.c @@ -24,6 +24,7 @@ #include "providers/krb5/krb5_common.h" #include "providers/krb5/krb5_auth.h" +#include "providers/krb5/krb5_utils.h" #include "providers/krb5/krb5_init_shared.h" errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx, @@ -90,6 +91,16 @@ errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx, goto done; } + ret = parse_krb5_map_user(krb5_auth_ctx, + dp_opt_get_cstring(krb5_auth_ctx->opts, + KRB5_MAP_USER), + &krb5_auth_ctx->name_to_primary); + 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 db62cc3..50d701b 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/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c index de2d945..0b73880 100644 --- a/src/providers/krb5/krb5_utils.c +++ b/src/providers/krb5/krb5_utils.c @@ -465,3 +465,117 @@ errno_t get_domain_or_subdomain(struct be_ctx *be_ctx, return EOK; } + +static errno_t split_tuple(TALLOC_CTX *mem_ctx, const char *tuple, + const char **_first, const char **_second) +{ + errno_t ret; + char **list; + int n; + + ret = split_on_separator(mem_ctx, tuple, ':', true, true, &list, &n); + + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "split_on_separator failed - %s:[%d]\n", + sss_strerror(ret), ret); + goto done; + } else if (n != 2) { + DEBUG(SSSDBG_MINOR_FAILURE, + "split_on_separator failed - Expected format is: " + "'username:primary' but got: '%s'.\n", tuple); + ret = EINVAL; + goto done; + } + + *_first = list[0]; + *_second = list[1]; + +done: + return ret; +} + +static errno_t +fill_name_to_primary_map(TALLOC_CTX *mem_ctx, char **map, + struct map_id_name_to_krb_primary *name_to_primary, + size_t size) +{ + int i; + errno_t ret; + + for (i = 0; i < size; i++) { + ret = split_tuple(mem_ctx, map[i], + &name_to_primary[i].id_name, + &name_to_primary[i].krb_primary); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "split_tuple failed - %s:[%d]\n", sss_strerror(ret), ret); + goto done; + } + } + + ret = EOK; + +done: + return ret; +} + +errno_t +parse_krb5_map_user(TALLOC_CTX *mem_ctx, const char *krb5_map_user, + struct map_id_name_to_krb_primary **_name_to_primary) +{ + int size; + char **map; + errno_t ret; + TALLOC_CTX *tmp_ctx; + struct map_id_name_to_krb_primary *name_to_primary; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + if (krb5_map_user == NULL || strlen(krb5_map_user) == 0) { + DEBUG(SSSDBG_FUNC_DATA, "Warning: krb5_map_user is empty!\n"); + size = 0; + } else { + 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_primary = talloc_zero_array(tmp_ctx, + struct map_id_name_to_krb_primary, + size + 1); + if (name_to_primary == NULL) { + ret = ENOMEM; + goto done; + } + /* sentinel */ + name_to_primary[size].id_name = NULL; + name_to_primary[size].krb_primary = NULL; + + if (size > 0) { + ret = fill_name_to_primary_map(name_to_primary, map, name_to_primary, + size); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "fill_name_to_primary_map failed: %s:[%d]\n", + sss_strerror(ret), ret); + goto done; + } + } + + ret = EOK; + +done: + if (ret == EOK) { + *_name_to_primary = talloc_steal(mem_ctx, name_to_primary); + } + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h index 0155905..75b93c3 100644 --- a/src/providers/krb5/krb5_utils.h +++ b/src/providers/krb5/krb5_utils.h @@ -49,4 +49,9 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr, errno_t get_domain_or_subdomain(struct be_ctx *be_ctx, char *domain_name, struct sss_domain_info **dom); + +errno_t +parse_krb5_map_user(TALLOC_CTX *mem_ctx, const char *krb5_map_user, + struct map_id_name_to_krb_primary **_name_to_primary); + #endif /* __KRB5_UTILS_H__ */ diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c index 409c0f0..650ed48 100644 --- a/src/tests/krb5_utils-tests.c +++ b/src/tests/krb5_utils-tests.c @@ -29,6 +29,7 @@ #include "providers/krb5/krb5_utils.h" #include "providers/krb5/krb5_ccache.h" #include "providers/krb5/krb5_auth.h" +#include "util/sss_utf8.h" #include "tests/common.h" #define TESTS_PATH "tests_krb5_utils" @@ -574,6 +575,115 @@ START_TEST(test_compare_principal_realm) } END_TEST +static void +compare_map_id_name_to_krb_primary(struct map_id_name_to_krb_primary *a, + const char **str, + size_t len) +{ + int i = 0; + errno_t ret; + + while (a[i].id_name != NULL && a[i].krb_primary != NULL) { + fail_unless(i < len); + ret = sss_utf8_case_eq((const uint8_t*)a[i].id_name, + (const uint8_t*)str[i*2]); + fail_unless(ret == EOK, + "%s does not match %s", a[i].id_name, str[i*2]); + + ret = sss_utf8_case_eq((const uint8_t*)a[i].krb_primary, + (const uint8_t*)str[i*2+1]); + fail_unless(ret == EOK, "%s does not match %s", + a[i].krb_primary, str[i*2+1]); + i++; + } + fail_unless(len == i, "%u != %u", len, i); +} + +START_TEST(test_parse_krb5_map_user) +{ + errno_t ret; + TALLOC_CTX *mem_ctx; + struct map_id_name_to_krb_primary *name_to_primary; + + mem_ctx = talloc_new(NULL); + + /* empty input */ + { + check_leaks_push(mem_ctx); + ret = parse_krb5_map_user(mem_ctx, NULL, &name_to_primary); + fail_unless(ret == EOK); + fail_unless(name_to_primary[0].id_name == NULL && + name_to_primary[0].krb_primary == NULL); + talloc_free(name_to_primary); + + ret = parse_krb5_map_user(mem_ctx, "", &name_to_primary); + fail_unless(ret == EOK); + fail_unless(name_to_primary[0].id_name == NULL && + name_to_primary[0].krb_primary == NULL); + talloc_free(name_to_primary); + + ret = parse_krb5_map_user(mem_ctx, ",", &name_to_primary); + fail_unless(ret == EOK); + fail_unless(name_to_primary[0].id_name == NULL && + name_to_primary[0].krb_primary == NULL); + talloc_free(name_to_primary); + + ret = parse_krb5_map_user(mem_ctx, ",,", &name_to_primary); + fail_unless(ret == EOK); + fail_unless(name_to_primary[0].id_name == NULL && + name_to_primary[0].krb_primary == NULL); + talloc_free(name_to_primary); + + check_leaks_pop(mem_ctx); + } + /* valid input */ + { + check_leaks_push(mem_ctx); + const char *p = "pája:preichl,joe:juser,jdoe:ßlack"; + const char *p2 = " pája : preichl , joe:\njuser,jdoe\t: ßlack "; + const char *expected[] = {"pája", "preichl", "joe", "juser", "jdoe", "ßlack"}; + ret = parse_krb5_map_user(mem_ctx, p, &name_to_primary); + fail_unless(ret == EOK); + compare_map_id_name_to_krb_primary(name_to_primary, expected, + sizeof(expected)/sizeof(const char*)/2); + talloc_free(name_to_primary); + + ret = parse_krb5_map_user(mem_ctx, p2, &name_to_primary); + fail_unless(ret == EOK); + compare_map_id_name_to_krb_primary(name_to_primary, expected, + sizeof(expected)/sizeof(const char*)/2); + talloc_free(name_to_primary); + check_leaks_pop(mem_ctx); + } + /* invalid input */ + { + check_leaks_push(mem_ctx); + + ret = parse_krb5_map_user(mem_ctx, ":", &name_to_primary); + fail_unless(ret == EINVAL); + + ret = parse_krb5_map_user(mem_ctx, "joe:", &name_to_primary); + fail_unless(ret == EINVAL); + + ret = parse_krb5_map_user(mem_ctx, ":joe", &name_to_primary); + fail_unless(ret == EINVAL); + + ret = parse_krb5_map_user(mem_ctx, "joe:,", &name_to_primary); + fail_unless(ret == EINVAL); + + ret = parse_krb5_map_user(mem_ctx, ",joe", &name_to_primary); + fail_unless(ret == EINVAL); + + ret = parse_krb5_map_user(mem_ctx, "joe:j:user", &name_to_primary); + fail_unless(ret == EINVAL); + + check_leaks_pop(mem_ctx); + } + + talloc_free(mem_ctx); +} +END_TEST + Suite *krb5_utils_suite (void) { Suite *s = suite_create ("krb5_utils"); @@ -612,6 +722,7 @@ Suite *krb5_utils_suite (void) TCase *tc_krb5_helpers = tcase_create("Helper functions"); tcase_add_test(tc_krb5_helpers, test_compare_principal_realm); + tcase_add_test(tc_krb5_helpers, test_parse_krb5_map_user); suite_add_tcase(s, tc_krb5_helpers); return s;