/* Copyright (C) 2012 the GSS-PROXY contributors, see COPYING for license */

#include "gss_plugin.h"

/* NOTE: The mech_invoke() methods are intrinsically intended to only
 * influence local process beahvior, or act on local process memory.
 * Therefore there is no point in even trying to proxy it. Always bridge
 * it back to the local mechanism. We'll handle specific desired_objects
 * in future if any exist that make sense to be proxied */
OM_uint32 gssi_mech_invoke(OM_uint32 *minor_status,
                           const gss_OID desired_mech,
                           const gss_OID desired_object,
                           gss_buffer_t value)
{
    OM_uint32 maj, min;

    GSSI_TRACE();

    maj = gssspi_mech_invoke(&min, gpp_special_mech(desired_mech),
                             desired_object, value);

    *minor_status = gpp_map_error(min);
    return maj;
}

/* NOTE: This call is currently useful only for the Spnego mech which we
 * never interpose */
#if 0
OM_uint32 gssi_set_neg_mechs(OM_uint32 *minor_status,
                             gss_cred_id_t cred_handle,
                             const gss_OID_set mech_set);
#endif

/* NOTE: I know of no mechanism that uses this yet, although NTLM might */
#if 0
OM_uint32 gssi_complete_auth_token(OM_uint32 *minor_status,
                                   const gss_ctx_id_t context_handle,
                                   gss_buffer_t input_message_buffer);
#endif

OM_uint32 gssi_localname(OM_uint32 *minor_status, const gss_name_t name,
                         gss_OID mech_type, gss_buffer_t localname)
{
    struct gpp_name_handle *gpname;
    OM_uint32 maj, min;

    GSSI_TRACE();

    *minor_status = 0;
    if (name == GSS_C_NO_NAME) {
        return GSS_S_CALL_INACCESSIBLE_READ;
    }

    gpname = (struct gpp_name_handle *)name;
    if (!gpname->local && !gpname->remote) {
        return GSS_S_CALL_INACCESSIBLE_READ;
    }

    if (gpname->remote) {
        maj = gpm_localname(&min, gpname->remote,
                            mech_type, localname);
        if (maj == GSS_S_COMPLETE) {
            goto done;
        } else if (maj == GSS_S_FAILURE && min == ENOTSUP) {
            /* fallback to local */
            if (!gpname->local) {
                maj = gpp_name_to_local(&min, gpname->remote,
                                        mech_type, &gpname->local);
                if (maj) {
                    goto done;
                }
            }
        }
    }
    if (gpname->local) {
        maj = gss_localname(&min, gpname->local,
                            gpp_special_mech(mech_type),
                            localname);
    }

done:
    *minor_status = gpp_map_error(min);
    return maj;
}

OM_uint32 gssi_authorize_localname(OM_uint32 *minor_status,
                                   const gss_name_t name,
                                   gss_buffer_t local_user,
                                   gss_OID local_nametype)
{
    struct gpp_name_handle *gpname;
    gss_name_t username = GSS_C_NO_NAME;
    OM_uint32 maj, min;

    GSSI_TRACE();

    *minor_status = 0;
    if (name == GSS_C_NO_NAME) {
        return GSS_S_CALL_INACCESSIBLE_READ;
    }

    /* FIXME: implement remote localname lookup ? Only local for now */
    gpname = (struct gpp_name_handle *)name;
    if (gpname->remote && !gpname->local) {
        maj = gpp_name_to_local(&min, gpname->remote,
                                gpname->mech_type, &gpname->local);
        if (maj != GSS_S_COMPLETE) {
            goto done;
        }
    }

    maj = gss_import_name(&min, local_user, local_nametype, &username);
    if (maj != GSS_S_COMPLETE) {
        goto done;
    }

    maj = gss_authorize_localname(&min, gpname->local, username);

done:
    *minor_status = gpp_map_error(min);
    (void)gss_release_name(&min, &username);
    return maj;
}

OM_uint32 gssi_map_name_to_any(OM_uint32 *minor_status, gss_name_t name,
                               int authenticated, gss_buffer_t type_id,
                               gss_any_t *output)
{
    struct gpp_name_handle *gpname;
    OM_uint32 maj, min;

    GSSI_TRACE();

    *minor_status = 0;
    if (name == GSS_C_NO_NAME) {
        return GSS_S_CALL_INACCESSIBLE_READ;
    }

    /* FIXME: implement remote localname lookup ? Only local for now */
    gpname = (struct gpp_name_handle *)name;
    if (gpname->remote && !gpname->local) {
        maj = gpp_name_to_local(&min, gpname->remote,
                                gpname->mech_type, &gpname->local);
        if (maj != GSS_S_COMPLETE) {
            goto done;
        }
    }

    maj = gss_map_name_to_any(&min, gpname->local,
                              authenticated, type_id, output);

done:
    *minor_status = gpp_map_error(min);
    return maj;
}

OM_uint32 gssi_release_any_name_mapping(OM_uint32 *minor_status,
                                        gss_name_t name,
                                        gss_buffer_t type_id,
                                        gss_any_t *input)
{
    struct gpp_name_handle *gpname;
    OM_uint32 maj, min;

    GSSI_TRACE();

    *minor_status = 0;
    if (name == GSS_C_NO_NAME) {
        return GSS_S_CALL_INACCESSIBLE_READ;
    }

    /* FIXME: implement remote localname lookup ? Only local for now */
    gpname = (struct gpp_name_handle *)name;
    if (!gpname->local) {
        return GSS_S_UNAVAILABLE;
    }

    maj = gss_release_any_name_mapping(&min, gpname->local, type_id, input);

    *minor_status = gpp_map_error(min);
    return maj;

}
