diff options
Diffstat (limited to 'lib/krb5_wrap/gss_samba.c')
-rw-r--r-- | lib/krb5_wrap/gss_samba.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/lib/krb5_wrap/gss_samba.c b/lib/krb5_wrap/gss_samba.c new file mode 100644 index 0000000..a594056 --- /dev/null +++ b/lib/krb5_wrap/gss_samba.c @@ -0,0 +1,222 @@ +/* + * Unix SMB/CIFS implementation. + * + * Simple GSSAPI wrappers + * + * Copyright (c) 2012 Andreas Schneider <asn@samba.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "gss_samba.h" + +#ifdef HAVE_GSSAPI + +#if !defined(HAVE_GSS_OID_EQUAL) +int smb_gss_oid_equal(const gss_OID first_oid, const gss_OID second_oid) +{ + if (first_oid == GSS_C_NO_OID || second_oid == GSS_C_NO_OID) { + return 0; + } + + if (first_oid == second_oid) { + return 1; + } + + if ((first_oid)->length != (second_oid)->length) { + return 0; + } + + if (memcmp((first_oid)->elements, (second_oid)->elements, + (first_oid)->length) == 0) { + return 1; + } + + return 0; +} +#endif /* !HAVE_GSS_OID_EQUAL */ + + +/* wrapper around gss_krb5_import_cred() that prefers to use gss_acquire_cred_from() + * if this GSSAPI extension is available. gss_acquire_cred_from() is properly + * interposed by GSSPROXY while gss_krb5_import_cred() is not. + * + * This wrapper requires a proper krb5_context to resolve ccache name. + * All gss_krb5_import_cred() callers in Samba already have krb5_context available. */ +uint32_t smb_gss_krb5_import_cred(uint32_t *minor_status, krb5_context ctx, + krb5_ccache id, krb5_principal keytab_principal, + krb5_keytab keytab, gss_cred_id_t *cred) +{ + uint32_t major_status = 0; + +#ifdef HAVE_GSS_ACQUIRE_CRED_FROM + uint32_t minor = 0; + gss_key_value_element_desc ccache_element = { + .key = "ccache", + .value = NULL, + }; + + gss_key_value_element_desc keytab_element = { + .key = "keytab", + .value = NULL, + }; + + gss_key_value_element_desc elements[2]; + + gss_key_value_set_desc cred_store = { + .elements = &ccache_element, + .count = 1, + }; + + /* we are interested exclusively in krb5 credentials, + * indicate to GSSAPI that we are not interested in any other + * mechanism here */ + gss_OID_set_desc mech_set = { + .count = 1, + .elements = discard_const_p(struct gss_OID_desc_struct, + gss_mech_krb5), + }; + + gss_cred_usage_t cred_usage = GSS_C_INITIATE; + gss_name_t name = NULL; + gss_buffer_desc pr_name = { + .value = NULL, + .length = 0, + }; + + if (id != NULL) { + major_status = krb5_cc_get_full_name(ctx, + id, + discard_const(&ccache_element.value)); + if (major_status != 0) { + return major_status; + } + } + + if (keytab != NULL) { + keytab_element.value = malloc(4096); + if (!keytab_element.value) { + return ENOMEM; + } + major_status = krb5_kt_get_name(ctx, + keytab, + discard_const(keytab_element.value), 4096); + if (major_status != 0) { + free(discard_const(keytab_element.value)); + return major_status; + } + cred_usage = GSS_C_ACCEPT; + cred_store.elements = &keytab_element; + + if (keytab_principal != NULL) { + major_status = krb5_unparse_name(ctx, keytab_principal, (char**)&pr_name.value); + if (major_status != 0) { + free(discard_const(keytab_element.value)); + return major_status; + } + pr_name.length = strlen(pr_name.value); + + major_status = gss_import_name(minor_status, + &pr_name, + discard_const(GSS_KRB5_NT_PRINCIPAL_NAME), + &name); + if (major_status != 0) { + krb5_free_unparsed_name(ctx, pr_name.value); + free(discard_const(keytab_element.value)); + return major_status; + } + } + } + + if (id != NULL && keytab != NULL) { + elements[0] = ccache_element; + elements[1] = keytab_element; + + cred_store.elements = elements; + cred_store.count = 2; + cred_usage = GSS_C_BOTH; + } + + major_status = gss_acquire_cred_from(minor_status, + name, + 0, + &mech_set, + cred_usage, + &cred_store, + cred, + NULL, + NULL); + + if (pr_name.value != NULL) { + (void)gss_release_name(&minor, &name); + krb5_free_unparsed_name(ctx, pr_name.value); + } + if (keytab_element.value != NULL) { + free(discard_const(keytab_element.value)); + } + krb5_free_string(ctx, discard_const(ccache_element.value)); +#else + major_status = gss_krb5_import_cred(minor_status, + id, + keytab_principal, + keytab, cred); + + if (major_status == (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME)) { + if ((keytab_principal == NULL) && (keytab != NULL)) { + /* No principal was specified and MIT krb5 1.9 version failed. + * We have to fall back to set global acceptor identity */ + gss_OID_set_desc mech_set; + char *kt_name = NULL; + + kt_name = malloc(4096); + if (!kt_name) { + return ENOMEM; + } + + major_status = krb5_kt_get_name(ctx, + keytab, + kt_name, 4096); + if (major_status != 0) { + free(kt_name); + return major_status; + } + + major_status = gsskrb5_register_acceptor_identity(kt_name); + if (major_status) { + free(kt_name); + return major_status; + } + + /* We are dealing with krb5 GSSAPI mech in this fallback */ + mech_set.count = 1; + mech_set.elements = + discard_const_p(struct gss_OID_desc_struct, + gss_mech_krb5); + major_status = gss_acquire_cred(minor_status, + GSS_C_NO_NAME, + GSS_C_INDEFINITE, + &mech_set, + GSS_C_ACCEPT, + cred, + NULL, NULL); + free(kt_name); + } + } +#endif + return major_status; +} + + +#endif /* HAVE_GSSAPI */ |