From a314a4a91648221ec5d60bc123ecedbf0f98ac78 Mon Sep 17 00:00:00 2001 From: Tomas Halman Date: Fri, 1 Apr 2022 19:06:59 +0200 Subject: [PATCH] GSS implementation in ldap_child --- Makefile.am | 8 +- src/providers/ldap/ldap_child.c | 364 ++++++++++++++++++++++++++---- src/tests/multihost/ad/pytest.ini | 1 + src/util/sss_gss.c | 309 +++++++++++++++++++++++++ src/util/sss_gss.h | 90 ++++++++ src/util/sss_krb5.c | 2 +- src/util/sss_krb5.h | 4 + 7 files changed, 733 insertions(+), 45 deletions(-) create mode 100644 src/util/sss_gss.c create mode 100644 src/util/sss_gss.h diff --git a/Makefile.am b/Makefile.am index 62b3e573cf8..0cc32bd6e72 100644 --- a/Makefile.am +++ b/Makefile.am @@ -700,6 +700,7 @@ dist_noinst_HEADERS = \ src/util/sss_python.h \ src/util/sss_regexp.h \ src/util/sss_krb5.h \ + src/util/sss_gss.h \ src/util/sss_selinux.h \ src/util/sss_sockets.h \ src/util/sss_utf8.h \ @@ -4560,6 +4561,7 @@ ldap_child_SOURCES = \ src/providers/ldap/ldap_child.c \ src/providers/krb5/krb5_keytab.c \ src/util/sss_krb5.c \ + src/util/sss_gss.c \ src/util/sss_iobuf.c \ src/util/atomic_io.c \ src/util/memory.c \ @@ -4574,13 +4576,15 @@ ldap_child_SOURCES = \ ldap_child_CFLAGS = \ $(AM_CFLAGS) \ $(POPT_CFLAGS) \ - $(KRB5_CFLAGS) + $(KRB5_CFLAGS) \ + $(GSSAPI_KRB5_CFLAGS) ldap_child_LDADD = \ libsss_debug.la \ $(TALLOC_LIBS) \ $(POPT_LIBS) \ $(DHASH_LIBS) \ - $(KRB5_LIBS) + $(KRB5_LIBS) \ + $(GSSAPI_KRB5_LIBS) if BUILD_SEMANAGE selinux_child_SOURCES = \ diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c index 4818240d4c9..21b6260c0c1 100644 --- a/src/providers/ldap/ldap_child.c +++ b/src/providers/ldap/ldap_child.c @@ -28,14 +28,19 @@ #include #include #include +#include +#include +#include #include "util/util.h" #include "util/sss_krb5.h" +#include "util/sss_gss.h" #include "util/child_common.h" #include "providers/backend.h" #include "providers/krb5/krb5_common.h" char *global_ccname_file_dummy = NULL; +bool use_gss = false; static void sig_term_handler(int sig) { @@ -258,6 +263,281 @@ static int lc_verify_keytab_ex(const char *principal, return EOK; } + + +static krb5_error_code ldap_child_get_realm(TALLOC_CTX *memctx, + TALLOC_CTX *tmp_ctx, + krb5_context context, + const char *realm_str, + char **_realm_name, + char **_krb5_msg) +{ + char *default_realm = NULL; + krb5_error_code krberr; + + *_realm_name = NULL; + if (!realm_str) { + krberr = krb5_get_default_realm(context, &default_realm); + if (krberr != 0) { + DEBUG(SSSDBG_OP_FAILURE, + "krb5_get_default_realm() failed: %d\n", krberr); + return krberr; + } + + *_realm_name = talloc_strdup(tmp_ctx, default_realm); + krb5_free_default_realm(context, default_realm); + if (!*_realm_name) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); + return krberr; + } + } else { + *_realm_name = talloc_strdup(tmp_ctx, realm_str); + if (!*_realm_name) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); + return krberr; + } + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "got realm_name: [%s]\n", *_realm_name); + return EOK; +} + +static int get_kdc_time_offset(krb5_context context) { +#ifdef HAVE_KRB5_GET_TIME_OFFSETS + krb5_timestamp kdc_time_offset; + int kdc_time_offset_usec; + krb5_error_code krberr; + + if (!context) { + return 0; + } + + krberr = krb5_get_time_offsets(context, &kdc_time_offset, + &kdc_time_offset_usec); + if (krberr != 0) { + const char *__err_msg = sss_krb5_get_error_message(context, krberr); + DEBUG(SSSDBG_OP_FAILURE, "Failed to get KDC time offset: %s\n", + __err_msg); + sss_krb5_free_error_message(context, __err_msg); + kdc_time_offset = 0; + } else { + if (kdc_time_offset_usec > 0) { + kdc_time_offset++; + } + } + DEBUG(SSSDBG_TRACE_INTERNAL, "Got KDC time offset\n"); + return kdc_time_offset; +#else + /* If we don't have this function, just assume no offset */ + return 0; +#endif +} + + +/* + * The ldap_child_get_tgt_gss_sync() function + * tries to get TGT using GSS API + * the result is stored in ccache. + */ +static krb5_error_code ldap_child_get_tgt_gss_sync(TALLOC_CTX *memctx, + krb5_context context, + const char *realm_str, + const char *princ_str, + const char *keytab_name, + const krb5_deltat lifetime, + const char **ccname_out, + time_t *expire_time_out, + char **_krb5_msg) +{ + char *ccname; + char *ccname_dummy; + char *realm_name = NULL; + char *full_principal = NULL; + krb5_keytab keytab = NULL; + krb5_ccache ccache = NULL; + krb5_principal kprinc; + krb5_error_code krberr; + krb5_timestamp kdc_time_offset; + errno_t error_code; + TALLOC_CTX *tmp_ctx; + char *ccname_file_dummy = NULL; + char *ccname_file; + + gss_name_t gss_name = GSS_C_NO_NAME; + gss_cred_id_t creds = NULL; + OM_uint32 major, minor, lifetime_out; + time_t ticket_eol; + + *_krb5_msg = NULL; + + tmp_ctx = talloc_new(memctx); + if (tmp_ctx == NULL) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); + goto done; + } + + error_code = set_child_debugging(context); + if (error_code != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set krb5_child debugging\n"); + } + + kdc_time_offset = get_kdc_time_offset(context); + + krberr = ldap_child_get_realm(memctx, tmp_ctx, context, realm_str, &realm_name, _krb5_msg); + if (krberr != 0) { + goto done; + } + + + if (princ_str) { + error_code = sss_gss_get_creds(memctx, + princ_str, realm_name, + lifetime, keytab_name, + &creds, &full_principal, + &lifetime_out); + } else { + char hostname[HOST_NAME_MAX + 1]; + + error_code = gethostname(hostname, sizeof(hostname)); + if (error_code == -1) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_asprintf(memctx, "hostname() failed: [%d][%s]", + errno, strerror(errno)); + goto done; + } + hostname[HOST_NAME_MAX] = '\0'; + + DEBUG(SSSDBG_TRACE_LIBS, "got hostname: [%s]\n", hostname); + + error_code = sss_gss_find_host_creds(memctx, + hostname, realm_name, + lifetime, keytab_name, + &creds, &full_principal, + &lifetime_out); + } + + if (error_code != EOK) { + krberr = KRB5_KT_IOERR; + *_krb5_msg = talloc_strdup(memctx, + "select_principal_from_keytab() failed"); + goto done; + } + ticket_eol = time(NULL) + lifetime_out; + + DEBUG(SSSDBG_TRACE_INTERNAL, "krb5_parse_name(%s)\n", full_principal); + krberr = krb5_parse_name(context, full_principal, &kprinc); + if (krberr != 0) { + DEBUG(SSSDBG_OP_FAILURE, "krb5_parse_name() failed: %d\n", krberr); + goto done; + } + + ccname_file = talloc_asprintf(tmp_ctx, "%s/ccache_%s", + DB_PATH, realm_name); + if (ccname_file == NULL) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); + goto done; + } + + ccname_file_dummy = talloc_asprintf(tmp_ctx, "%s/ccache_%s_XXXXXX", + DB_PATH, realm_name); + if (ccname_file_dummy == NULL) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); + goto done; + } + global_ccname_file_dummy = ccname_file_dummy; + + error_code = sss_unique_filename(tmp_ctx, ccname_file_dummy); + if (error_code != EOK) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_asprintf(memctx, + "sss_unique_filename() failed: [%d][%s]", + error_code, strerror(error_code)); + goto done; + } + + /* prepace new ccache */ + ccname_dummy = talloc_asprintf(tmp_ctx, "FILE:%s", ccname_file_dummy); + ccname = talloc_asprintf(tmp_ctx, "FILE:%s", ccname_file); + if (ccname_dummy == NULL || ccname == NULL) { + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); + goto done; + } + DEBUG(SSSDBG_TRACE_INTERNAL, "keytab ccname: [%s]\n", ccname_dummy); + + krberr = krb5_cc_resolve(context, ccname_dummy, &ccache); + if (krberr != 0) { + DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_resolve() failed: %d\n", krberr); + goto done; + } + + krberr = krb5_cc_initialize(context, ccache, kprinc); + if (krberr != 0) { + DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_initialize() failed: %d\n", krberr); + goto done; + } + + major = gss_krb5_copy_ccache(&minor, creds, ccache); + if (GSS_ERROR(major)) { + DEBUG(SSSDBG_OP_FAILURE,"gss_krb5_copy_ccache failed\n"); + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = sss_gss_error_message(memctx, major); + goto done; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Renaming [%s] to [%s]\n", ccname_file_dummy, ccname_file); + error_code = rename(ccname_file_dummy, ccname_file); + if (error_code == -1) { + error_code = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "rename failed [%d][%s].\n", error_code, strerror(error_code)); + krberr = KRB5KRB_ERR_GENERIC; + *_krb5_msg = talloc_asprintf(memctx, + "rename() failed: [%d][%s]", + error_code, strerror(error_code)); + + goto done; + } + global_ccname_file_dummy = NULL; + + krberr = 0; + *ccname_out = talloc_steal(memctx, ccname); + *expire_time_out = ticket_eol - kdc_time_offset; +done: + talloc_zfree(full_principal); + if (krberr != 0) { + if (*_krb5_msg == NULL) { + /* no custom error message provided hence get one from libkrb5 */ + const char *__krberr_msg = sss_krb5_get_error_message(context, krberr); + *_krb5_msg = talloc_strdup(memctx, __krberr_msg); + sss_krb5_free_error_message(context, __krberr_msg); + } + + sss_log(SSS_LOG_ERR, + "Failed to initialize credentials using keytab [%s]: %s. " + "Unable to create GSSAPI-encrypted LDAP connection.", + sss_printable_keytab_name(context, keytab_name), *_krb5_msg); + + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to initialize credentials using keytab [%s]: %s. " + "Unable to create GSSAPI-encrypted LDAP connection.\n", + sss_printable_keytab_name(context, keytab_name), *_krb5_msg); + } + if (keytab) krb5_kt_close(context, keytab); + if (context) krb5_free_context(context); + gss_release_name(&minor, &gss_name); + gss_release_cred(&minor, &creds); + talloc_free(tmp_ctx); + return krberr; +} + + static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx, krb5_context context, const char *realm_str, @@ -272,7 +552,6 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx, char *ccname_dummy; char *realm_name = NULL; char *full_princ = NULL; - char *default_realm = NULL; char *tmp_str = NULL; krb5_keytab keytab = NULL; krb5_ccache ccache = NULL; @@ -303,32 +582,11 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx, DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set krb5_child debugging\n"); } - if (!realm_str) { - krberr = krb5_get_default_realm(context, &default_realm); - if (krberr != 0) { - DEBUG(SSSDBG_OP_FAILURE, - "krb5_get_default_realm() failed: %d\n", krberr); - goto done; - } - - realm_name = talloc_strdup(tmp_ctx, default_realm); - krb5_free_default_realm(context, default_realm); - if (!realm_name) { - krberr = KRB5KRB_ERR_GENERIC; - *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); - goto done; - } - } else { - realm_name = talloc_strdup(tmp_ctx, realm_str); - if (!realm_name) { - krberr = KRB5KRB_ERR_GENERIC; - *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM)); - goto done; - } + krberr = ldap_child_get_realm(memctx, tmp_ctx, context, realm_str, &realm_name, _krb5_msg); + if (krberr != 0) { + goto done; } - DEBUG(SSSDBG_TRACE_INTERNAL, "got realm_name: [%s]\n", realm_name); - if (princ_str) { if (!strchr(princ_str, '@')) { full_princ = talloc_asprintf(tmp_ctx, "%s@%s", @@ -634,6 +892,11 @@ int main(int argc, const char *argv[]) struct response *resp = NULL; ssize_t written; + /* if GSS_USE_PROXY is set, we use GSS. No matter whether */ + /* it is set to "yes" or "no". To use direct access to keytab */ + /* unset this environment variable */ + use_gss = getenv("GSS_USE_PROXY") != NULL; + struct poptOption long_options[] = { POPT_AUTOHELP SSSD_DEBUG_OPTS @@ -725,27 +988,44 @@ int main(int argc, const char *argv[]) goto fail; } - kerr = privileged_krb5_setup(ibuf); - if (kerr != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "Privileged Krb5 setup failed.\n"); - goto fail; + if (use_gss) { + DEBUG(SSSDBG_TRACE_INTERNAL, "GSS: skipping priviledged initialization\n"); + kerr = sss_krb5_init_context(&ibuf->context); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init kerberos context\n"); + goto fail; + } + } else { + kerr = privileged_krb5_setup(ibuf); + if (kerr != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Privileged Krb5 setup failed.\n"); + goto fail; + } + DEBUG(SSSDBG_TRACE_INTERNAL, "Kerberos context initialized\n"); } - DEBUG(SSSDBG_TRACE_INTERNAL, "Kerberos context initialized\n"); - kerr = become_user(ibuf->uid, ibuf->gid); - if (kerr != 0) { - DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); - goto fail; + if (use_gss) { + DEBUG(SSSDBG_TRACE_INTERNAL, "getting TGT sync using gss\n"); + kerr = ldap_child_get_tgt_gss_sync(main_ctx, ibuf->context, + ibuf->realm_str, ibuf->princ_str, + ibuf->keytab_name, ibuf->lifetime, + &ccname, &expire_time, &krb5_msg); + } else { + kerr = become_user(ibuf->uid, ibuf->gid); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); + goto fail; + } + DEBUG(SSSDBG_TRACE_INTERNAL, + "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid()); + + DEBUG(SSSDBG_TRACE_INTERNAL, "getting TGT sync\n"); + kerr = ldap_child_get_tgt_sync(main_ctx, ibuf->context, + ibuf->realm_str, ibuf->princ_str, + ibuf->keytab_name, ibuf->lifetime, + &ccname, &expire_time, &krb5_msg); } - DEBUG(SSSDBG_TRACE_INTERNAL, - "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid()); - - DEBUG(SSSDBG_TRACE_INTERNAL, "getting TGT sync\n"); - kerr = ldap_child_get_tgt_sync(main_ctx, ibuf->context, - ibuf->realm_str, ibuf->princ_str, - ibuf->keytab_name, ibuf->lifetime, - &ccname, &expire_time, &krb5_msg); if (kerr != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_child_get_tgt_sync failed.\n"); /* Do not return, must report failure */ diff --git a/src/tests/multihost/ad/pytest.ini b/src/tests/multihost/ad/pytest.ini index f64e0d89b89..da8ed180b4e 100644 --- a/src/tests/multihost/ad/pytest.ini +++ b/src/tests/multihost/ad/pytest.ini @@ -16,3 +16,4 @@ markers = tier1_3: tier1 test cases split to keep runtime upto 60 minutes tier2: All tier2 test cases tier3: All tier3 test cases + admisc: Misc ad test diff --git a/src/util/sss_gss.c b/src/util/sss_gss.c new file mode 100644 index 00000000000..40719f69b04 --- /dev/null +++ b/src/util/sss_gss.c @@ -0,0 +1,309 @@ +/* + Authors: + Tomas Halman + + Copyright (C) 2022 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "sss_gss.h" + +#include +#include + +#include "util/sss_krb5.h" + +/* obtain error message from gss api */ +char *sss_gss_error_message(TALLOC_CTX *ctx, OM_uint32 status_code) +{ + OM_uint32 message_context; + OM_uint32 maj_status; + OM_uint32 min_status; + gss_buffer_desc status_string; + char *message = NULL; + + message_context = 0; + + do { + + maj_status = gss_display_status( + &min_status, + status_code, + GSS_C_GSS_CODE, + GSS_C_NO_OID, + &message_context, + &status_string); + if (GSS_ERROR(maj_status)) { + DEBUG(SSSDBG_OP_FAILURE, "Error while reading GSS message (maj: %u, min: %u)\n", maj_status, min_status); + return message; + } + if (message) { + message = talloc_asprintf_append(message, "%.*s", + (int)status_string.length, + (char *)status_string.value); + } else { + message = talloc_asprintf(ctx, "%.*s", + (int)status_string.length, + (char *)status_string.value); + } + if (message == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Error while reading GSS message - out of memory\n"); + return NULL; + } + + gss_release_buffer(&min_status, &status_string); + } while (message_context != 0); + + return message; +} + + +/* get principal from gss gredentials */ +char *sss_gss_get_principal_from_creds(TALLOC_CTX *ctx, gss_cred_id_t creds) +{ + OM_uint32 major; + OM_uint32 minor; + gss_buffer_desc gss_name_buf; + gss_name_t gss_name = GSS_C_NO_NAME; + char *result = NULL; + + major = gss_inquire_cred(&minor, creds, &gss_name, NULL, NULL, NULL); + if (GSS_ERROR(major)) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not get principal from credentials - " + "gss_inquire_cred failed\n"); + goto done; + } + + major = gss_display_name(&minor, gss_name, &gss_name_buf, NULL); + if (GSS_ERROR(major)) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not get principal from credentials - " + "gss_display_name failed\n"); + goto done; + } + + result = talloc_asprintf(ctx, + "%.*s", + (int)gss_name_buf.length, + (char *)gss_name_buf.value); + +done: + major = gss_release_buffer(&minor, &gss_name_buf); + if (GSS_ERROR(major)) { + DEBUG(SSSDBG_MINOR_FAILURE, "The gss_release_buffer failed\n"); + } + + return result; +} + +/* acquire credentials from keytab */ +errno_t sss_gss_get_creds(TALLOC_CTX *ctx, + const char *principal, + const char *realm, + OM_uint32 lifetime, + const char *keytab_name, + gss_cred_id_t *_creds, + char **_principal, + OM_uint32 *_lifetime_out) +{ + gss_buffer_desc name_buf; + gss_name_t gss_name = GSS_C_NO_NAME; + gss_key_value_set_desc cstore; + OM_uint32 major; + OM_uint32 minor; + gss_OID_set_desc krb5_set = {1, gss_mech_krb5}; + char *full_principal = NULL; + errno_t ret = EOK; + + if (principal == NULL || _principal == NULL || + _creds == NULL || _lifetime_out == NULL) { + return EINVAL; + } + + *_principal = NULL; + + if (principal == NULL) { + ret = EINVAL; + goto done; + } + + if (strchr(principal, '@') || realm == NULL) { + full_principal = talloc_strdup(ctx, principal); + } else { + full_principal = talloc_asprintf(ctx, "%s@%s", + principal, realm); + } + if (!full_principal) { + ret = ENOMEM; + goto done; + } + + name_buf.value = (void *)(uintptr_t)full_principal; + name_buf.length = strlen(full_principal); + major = gss_import_name(&minor, &name_buf, GSS_C_NT_USER_NAME, &gss_name); + if (GSS_ERROR(major)) { + DEBUG(SSSDBG_OP_FAILURE, "Could not convert %s to GSS name\n", full_principal); + ret = EIO; + goto done; + } + + cstore.elements = talloc_array(ctx, struct gss_key_value_element_struct, 1); + cstore.elements[0].key = "client_keytab"; + cstore.elements[0].value = keytab_name ? keytab_name : "/etc/krb5.keytab"; + cstore.count = 1; + major = gss_acquire_cred_from(&minor, gss_name, lifetime, + &krb5_set, GSS_C_INITIATE, + &cstore, _creds, NULL, _lifetime_out); + talloc_free(cstore.elements); + + if (GSS_ERROR(major)) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not acquire credentials for %s from %s: GSS message: %s\n", + full_principal, + keytab_name ? keytab_name : "default keytab", + sss_gss_error_message(ctx, major)); + ret = EIO; + goto done; + } + + *_principal = sss_gss_get_principal_from_creds(ctx, *_creds); + if (*_principal == NULL) { + /* getting principal from credentials failed lets use the input */ + /* value to have at least something */ + *_principal = full_principal; + full_principal = NULL; + } + + DEBUG(SSSDBG_TRACE_LIBS, "GSS acquire credentials for %s OK\n", full_principal); + + done: + gss_release_name(&minor, &gss_name); + talloc_free(full_principal); + return ret; +} + + +static errno_t sss_compose_principal(TALLOC_CTX *ctx, + const char *primary_pattern, + const char *realm_pattern, + const char *hostname, + const char *realm, + char **_full_principal) +{ + char *primary_part = NULL; + char *realm_part = NULL; + errno_t ret = EOK; + + *_full_principal = NULL; + + if (primary_pattern == NULL || hostname == NULL || realm == NULL) { + ret = EINVAL; + goto done; + } + + primary_part = sss_krb5_get_primary(ctx, + primary_pattern, + hostname); + if (primary_part == NULL) { + ret = ENOMEM; + goto done; + } + + if (realm_pattern) { + realm_part = talloc_asprintf(ctx, realm_pattern, realm); + if (realm_part == NULL) { + ret = ENOMEM; + goto done; + } + } + + if (realm_part == NULL) { + *_full_principal = primary_part; + primary_part = NULL; + } else { + *_full_principal = talloc_asprintf(ctx, "%s@%s", primary_part, realm_part); + if (*_full_principal == NULL) { + ret = ENOMEM; + goto done; + } + } + done: + talloc_free(primary_part); + talloc_free(realm_part); + return ret; +} + + +/* find suitable credentials from keytab */ +errno_t sss_gss_find_host_creds(TALLOC_CTX *ctx, + const char *hostname, + const char *realm, + int lifetime, + const char *keytab_name, + gss_cred_id_t *_creds, + char **_principal, + OM_uint32 *_lifetime) +{ + char *full_principal; + errno_t ret; + int idx = 0; + + /** + * The %s conversion is passed as-is, the %S conversion is translated to + * "short host name" + * + * Priority of lookup: + * - our.hostname@REALM or host/our.hostname@REALM depending on the input + * - SHORT.HOSTNAME$@REALM (AD domain) + * - host/our.hostname@REALM + * - pick the default principal in the keytab + * GSS does not allow us to iterate keytab so in unlike in the direct keytab access + * we skip lookups for + * - foobar$@REALM (AD domain) + * - host/foobar@REALM + * - host/foo@BAR + */ + const char *primary_patterns[] = {"%s", "%S$", "host/%s", NULL}; + const char *realm_patterns[] = {"%s", "%s", "%s", NULL}; + + do { + ret = sss_compose_principal(ctx, + primary_patterns[idx], + realm_patterns[idx], + hostname, + realm, + &full_principal); + if (ret != EOK) { + talloc_zfree(full_principal); + return ret; + } + + ret = sss_gss_get_creds(ctx, + full_principal, + NULL, + lifetime, + keytab_name, + _creds, + _principal, + _lifetime); + talloc_zfree(full_principal); + if (ret == EOK) { + break; + } + + ++idx; + } while(primary_patterns[idx-1] != NULL || realm_patterns[idx-1] != NULL); + + return ret; +} diff --git a/src/util/sss_gss.h b/src/util/sss_gss.h new file mode 100644 index 00000000000..2cf1111e389 --- /dev/null +++ b/src/util/sss_gss.h @@ -0,0 +1,90 @@ +/* + Authors: + Tomas Halman + + Copyright (C) 2022 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _SSS_GSS_H_ +#define _SSS_GSS_H_ + +#include +#include +#include + +#include "util/util.h" + +/** + * Get the error message string from GSS API + */ +char *sss_gss_error_message(TALLOC_CTX *ctx, OM_uint32 status_code); + +/** + * Extract principal from gss gredentials + */ +char *sss_gss_get_principal_from_creds(TALLOC_CTX *ctx, gss_cred_id_t creds); + + +/** + * Get credentials using GSS API + * Parameters: + * principal - principal name (can be just "name" or "name@REALM") + * realm - kreberos realm. It is ignored if the realm is part of principal + * lifetime - desired lifetime + * keytab_name - keytab to use (/etc/krb5.keytab if null) + * + * output parameters: + * _creds - obtained GSS credentials + * _principal - principal name associated with _creds + * _lifetime_out - credentials actual lifetime (can differ from lifetime) + * + * Returns EOK in case of success + */ +errno_t sss_gss_get_creds(TALLOC_CTX *ctx, + const char *principal, + const char *realm, + OM_uint32 lifetime, + const char *keytab_name, + gss_cred_id_t *_creds, + char **_principal, + OM_uint32 *_lifetime_out); + +/** + * Find suitable credentials using GSS API + * Parameters: + * hostname - computer hostname + * realm - kreberos realm + * lifetime - desired lifetime + * keytab_name - keytab to use (/etc/krb5.keytab if null) + * + * output parameters: + * _creds - obtained GSS credentials + * _principal - principal name associated with _creds + * _lifetime_out - credentials actual lifetime (can differ from lifetime) + * + * Returns EOK in case of success + */ +errno_t sss_gss_find_host_creds(TALLOC_CTX *ctx, + const char *hostname, + const char *realm, + int lifetime, + const char *keytab_name, + gss_cred_id_t *_creds, + char **_principal, + OM_uint32 *_lifetime); + + +#endif /* _SSS_GSS_H_ */ diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c index 6c4a908b74b..19ade3e56eb 100644 --- a/src/util/sss_krb5.c +++ b/src/util/sss_krb5.c @@ -30,7 +30,7 @@ #include "util/util_errors.h" #include "util/sss_krb5.h" -static char * +char * sss_krb5_get_primary(TALLOC_CTX *mem_ctx, const char *pattern, const char *hostname) diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h index a9254e21010..76242957357 100644 --- a/src/util/sss_krb5.h +++ b/src/util/sss_krb5.h @@ -51,6 +51,10 @@ const char *sss_printable_keytab_name(krb5_context ctx, const char *keytab_name) #define HAVE_KRB5_CC_COLLECTION 1 #endif +char *sss_krb5_get_primary(TALLOC_CTX *mem_ctx, + const char *pattern, + const char *hostname); + const char * KRB5_CALLCONV sss_krb5_get_error_message (krb5_context, krb5_error_code);