2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright 2009 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A#include "k5-int.h"
2N/A#include "gssapiP_krb5.h"
2N/A#ifdef HAVE_MEMORY_H
2N/A#include <memory.h>
2N/A#endif
2N/A#include <assert.h>
2N/A
2N/Astatic OM_uint32
2N/Akg_set_desired_mechs(OM_uint32 *minor_status,
2N/A const gss_OID_set desired_mechs,
2N/A krb5_gss_cred_id_t cred)
2N/A{
2N/A unsigned int i;
2N/A
2N/A if (desired_mechs == GSS_C_NULL_OID_SET) {
2N/A cred->prerfc_mech = 1;
2N/A cred->rfc_mech = 1;
2N/A } else {
2N/A cred->prerfc_mech = 0;
2N/A cred->rfc_mech = 0;
2N/A
2N/A for (i = 0; i < desired_mechs->count; i++) {
2N/A if (g_OID_equal(gss_mech_krb5_old, &desired_mechs->elements[i]))
2N/A cred->prerfc_mech = 1;
2N/A else if (g_OID_equal(gss_mech_krb5, &desired_mechs->elements[i]))
2N/A cred->rfc_mech = 1;
2N/A }
2N/A
2N/A if (!cred->prerfc_mech && !cred->rfc_mech) {
2N/A *minor_status = 0;
2N/A return GSS_S_BAD_MECH;
2N/A }
2N/A }
2N/A
2N/A return GSS_S_COMPLETE;
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Akg_return_mechs(OM_uint32 *minor_status,
2N/A krb5_gss_cred_id_t cred,
2N/A gss_OID_set *actual_mechs)
2N/A{
2N/A OM_uint32 major_status, minor;
2N/A gss_OID_set mechs;
2N/A
2N/A if (actual_mechs == NULL)
2N/A return GSS_S_COMPLETE;
2N/A
2N/A major_status = generic_gss_create_empty_oid_set(minor_status, &mechs);
2N/A if (GSS_ERROR(major_status))
2N/A return major_status;
2N/A
2N/A if (cred->prerfc_mech) {
2N/A major_status = generic_gss_add_oid_set_member(minor_status,
2N/A gss_mech_krb5_old,
2N/A &mechs);
2N/A if (GSS_ERROR(major_status)) {
2N/A generic_gss_release_oid_set(&minor, &mechs);
2N/A return major_status;
2N/A }
2N/A }
2N/A if (cred->rfc_mech) {
2N/A major_status = generic_gss_add_oid_set_member(minor_status,
2N/A gss_mech_krb5,
2N/A &mechs);
2N/A if (GSS_ERROR(major_status)) {
2N/A generic_gss_release_oid_set(&minor, &mechs);
2N/A return major_status;
2N/A }
2N/A }
2N/A
2N/A *actual_mechs = mechs;
2N/A
2N/A return GSS_S_COMPLETE;
2N/A}
2N/A
2N/Astatic int
2N/Akg_is_initiator_cred(krb5_gss_cred_id_t cred)
2N/A{
2N/A return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH) &&
2N/A (cred->ccache != NULL);
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Akg_impersonate_name(OM_uint32 *minor_status,
2N/A const krb5_gss_cred_id_t impersonator_cred,
2N/A const krb5_gss_name_t user,
2N/A OM_uint32 time_req,
2N/A const gss_OID_set desired_mechs,
2N/A krb5_gss_cred_id_t *output_cred,
2N/A gss_OID_set *actual_mechs,
2N/A OM_uint32 *time_rec,
2N/A krb5_context context)
2N/A{
2N/A OM_uint32 major_status;
2N/A krb5_error_code code;
2N/A krb5_creds in_creds, *out_creds = NULL;
2N/A
2N/A memset(&in_creds, 0, sizeof(in_creds));
2N/A memset(&out_creds, 0, sizeof(out_creds));
2N/A
2N/A in_creds.client = user->princ;
2N/A in_creds.server = impersonator_cred->name->princ;
2N/A
2N/A if (impersonator_cred->req_enctypes != NULL)
2N/A in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0];
2N/A
2N/A code = k5_mutex_lock(&user->lock);
2N/A if (code != 0) {
2N/A *minor_status = code;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A if (user->ad_context != NULL) {
2N/A code = krb5_authdata_export_authdata(context,
2N/A user->ad_context,
2N/A AD_USAGE_TGS_REQ,
2N/A &in_creds.authdata);
2N/A if (code != 0) {
2N/A k5_mutex_unlock(&user->lock);
2N/A *minor_status = code;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A }
2N/A
2N/A k5_mutex_unlock(&user->lock);
2N/A
2N/A code = krb5_get_credentials_for_user(context,
2N/A KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE,
2N/A impersonator_cred->ccache,
2N/A &in_creds,
2N/A NULL, &out_creds);
2N/A if (code != 0) {
2N/A krb5_free_authdata(context, in_creds.authdata);
2N/A *minor_status = code;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A major_status = kg_compose_deleg_cred(minor_status,
2N/A impersonator_cred,
2N/A out_creds,
2N/A time_req,
2N/A desired_mechs,
2N/A output_cred,
2N/A actual_mechs,
2N/A time_rec,
2N/A context);
2N/A
2N/A krb5_free_authdata(context, in_creds.authdata);
2N/A krb5_free_creds(context, out_creds);
2N/A
2N/A return major_status;
2N/A}
2N/A
2N/AOM_uint32
2N/Akrb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2N/A const gss_cred_id_t impersonator_cred_handle,
2N/A const gss_name_t desired_name,
2N/A OM_uint32 time_req,
2N/A const gss_OID_set desired_mechs,
2N/A gss_cred_usage_t cred_usage,
2N/A gss_cred_id_t *output_cred_handle,
2N/A gss_OID_set *actual_mechs,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A OM_uint32 major_status;
2N/A krb5_error_code code;
2N/A krb5_gss_cred_id_t cred;
2N/A krb5_context context;
2N/A
2N/A if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
2N/A return GSS_S_CALL_INACCESSIBLE_READ;
2N/A
2N/A if (desired_name == GSS_C_NO_NAME)
2N/A return GSS_S_CALL_INACCESSIBLE_READ;
2N/A
2N/A if (output_cred_handle == NULL)
2N/A return GSS_S_CALL_INACCESSIBLE_WRITE;
2N/A
2N/A if (cred_usage != GSS_C_INITIATE) {
2N/A *minor_status = (OM_uint32)G_BAD_USAGE;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A *output_cred_handle = GSS_C_NO_CREDENTIAL;
2N/A if (actual_mechs != NULL)
2N/A *actual_mechs = GSS_C_NO_OID_SET;
2N/A if (time_rec != NULL)
2N/A *time_rec = 0;
2N/A
2N/A code = krb5_gss_init_context(&context);
2N/A if (code != 0) {
2N/A *minor_status = code;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A major_status = krb5_gss_validate_cred_1(minor_status,
2N/A impersonator_cred_handle,
2N/A context);
2N/A if (GSS_ERROR(major_status)) {
2N/A krb5_free_context(context);
2N/A return major_status;
2N/A }
2N/A
2N/A major_status = kg_impersonate_name(minor_status,
2N/A (krb5_gss_cred_id_t)impersonator_cred_handle,
2N/A (krb5_gss_name_t)desired_name,
2N/A time_req,
2N/A desired_mechs,
2N/A &cred,
2N/A actual_mechs,
2N/A time_rec,
2N/A context);
2N/A
2N/A *output_cred_handle = (gss_cred_id_t)cred;
2N/A
2N/A k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock);
2N/A krb5_free_context(context);
2N/A
2N/A return major_status;
2N/A
2N/A}
2N/A
2N/AOM_uint32
2N/Akg_compose_deleg_cred(OM_uint32 *minor_status,
2N/A krb5_gss_cred_id_t impersonator_cred,
2N/A krb5_creds *subject_creds,
2N/A OM_uint32 time_req,
2N/A const gss_OID_set desired_mechs,
2N/A krb5_gss_cred_id_t *output_cred,
2N/A gss_OID_set *actual_mechs,
2N/A OM_uint32 *time_rec,
2N/A krb5_context context)
2N/A{
2N/A OM_uint32 major_status;
2N/A krb5_error_code code;
2N/A krb5_gss_cred_id_t cred = NULL;
2N/A
2N/A k5_mutex_assert_locked(&impersonator_cred->lock);
2N/A
2N/A if (!kg_is_initiator_cred(impersonator_cred) ||
2N/A impersonator_cred->name == NULL ||
2N/A impersonator_cred->proxy_cred) {
2N/A code = G_BAD_USAGE;
2N/A goto cleanup;
2N/A }
2N/A
2N/A assert(impersonator_cred->name->princ != NULL);
2N/A
2N/A assert(subject_creds != NULL);
2N/A assert(subject_creds->client != NULL);
2N/A
2N/A cred = xmalloc(sizeof(*cred));
2N/A if (cred == NULL) {
2N/A code = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A memset(cred, 0, sizeof(*cred));
2N/A
2N/A code = k5_mutex_init(&cred->lock);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A
2N/A /*
2N/A * Only return a "proxy" credential for use with constrained
2N/A * delegation if the subject credentials are forwardable.
2N/A * Submitting non-forwardable credentials to the KDC for use
2N/A * with constrained delegation will only return an error.
2N/A */
2N/A cred->usage = GSS_C_INITIATE;
2N/A cred->proxy_cred = !!(subject_creds->ticket_flags & TKT_FLG_FORWARDABLE);
2N/A
2N/A major_status = kg_set_desired_mechs(minor_status, desired_mechs, cred);
2N/A if (GSS_ERROR(major_status))
2N/A goto cleanup;
2N/A
2N/A cred->tgt_expire = impersonator_cred->tgt_expire;
2N/A
2N/A code = kg_init_name(context, subject_creds->client, NULL, 0, &cred->name);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A
2N/A code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A
2N/A code = krb5_cc_initialize(context, cred->ccache,
2N/A cred->proxy_cred ? impersonator_cred->name->princ :
2N/A subject_creds->client);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A
2N/A if (cred->proxy_cred) {
2N/A /* Impersonator's TGT will be necessary for S4U2Proxy */
2N/A code = krb5_cc_copy_creds(context, impersonator_cred->ccache,
2N/A cred->ccache);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A }
2N/A
2N/A code = krb5_cc_store_cred(context, cred->ccache, subject_creds);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A
2N/A if (time_rec != NULL) {
2N/A krb5_timestamp now;
2N/A
2N/A code = krb5_timeofday(context, &now);
2N/A if (code != 0)
2N/A goto cleanup;
2N/A
2N/A *time_rec = cred->tgt_expire - now;
2N/A }
2N/A
2N/A major_status = kg_return_mechs(minor_status, cred, actual_mechs);
2N/A if (GSS_ERROR(major_status))
2N/A goto cleanup;
2N/A
2N/A if (!kg_save_cred_id((gss_cred_id_t)cred)) {
2N/A code = G_VALIDATE_FAILED;
2N/A goto cleanup;
2N/A }
2N/A
2N/A major_status = GSS_S_COMPLETE;
2N/A *minor_status = 0;
2N/A *output_cred = cred;
2N/A
2N/Acleanup:
2N/A if (code != 0) {
2N/A *minor_status = code;
2N/A major_status = GSS_S_FAILURE;
2N/A }
2N/A
2N/A if (GSS_ERROR(major_status) && cred != NULL) {
2N/A k5_mutex_destroy(&cred->lock);
2N/A krb5_cc_destroy(context, cred->ccache);
2N/A kg_release_name(context, 0, &cred->name);
2N/A xfree(cred);
2N/A }
2N/A
2N/A return major_status;
2N/A}