/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/gssapi/mechglue/g_export_cred.c - gss_export_cred definition */
/*
 * Copyright (C) 2012 by the Massachusetts Institute of Technology.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "mglueP.h"

static OM_uint32
val_exp_cred_args(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
                  gss_buffer_t token)
{

    /* Initialize outputs. */
    if (minor_status != NULL)
        *minor_status = 0;
    if (token != GSS_C_NO_BUFFER) {
        token->length = 0;
        token->value = NULL;
    }

    /* Validate arguments. */
    if (minor_status == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;
    if (cred_handle == GSS_C_NO_CREDENTIAL)
        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED;
    if (token == GSS_C_NO_BUFFER)
        return GSS_S_CALL_INACCESSIBLE_WRITE;
    return GSS_S_COMPLETE;
}

OM_uint32 KRB5_CALLCONV
gss_export_cred(OM_uint32 * minor_status, gss_cred_id_t cred_handle,
                gss_buffer_t token)
{
    OM_uint32 status, tmpmin;
    gss_union_cred_t cred;
    gss_OID mech_oid;
    gss_OID public_oid;
    gss_mechanism mech;
    gss_buffer_desc mech_token;
    struct k5buf buf;
    char lenbuf[4];
    int i;

    status = val_exp_cred_args(minor_status, cred_handle, token);
    if (status != GSS_S_COMPLETE)
        return status;

    k5_buf_init_dynamic(&buf);

    cred = (gss_union_cred_t) cred_handle;
    for (i = 0; i < cred->count; i++) {
        /* Get an export token for this mechanism. */
        mech_oid = &cred->mechs_array[i];
        public_oid = gssint_get_public_oid(mech_oid);
        mech = gssint_get_mechanism(mech_oid);
        if (mech == NULL) {
            status = GSS_S_DEFECTIVE_CREDENTIAL;
            goto error;
        }
        if (mech->gss_export_cred == NULL) {
            status = GSS_S_UNAVAILABLE;
            goto error;
        }
        status = mech->gss_export_cred(minor_status, cred->cred_array[i],
                                       &mech_token);
        if (status != GSS_S_COMPLETE) {
            map_error(minor_status, mech);
            goto error;
        }

        /* Append the mech OID and token to buf. */
        store_32_be(public_oid->length, lenbuf);
        k5_buf_add_len(&buf, lenbuf, 4);
        k5_buf_add_len(&buf, public_oid->elements, public_oid->length);
        store_32_be(mech_token.length, lenbuf);
        k5_buf_add_len(&buf, lenbuf, 4);
        k5_buf_add_len(&buf, mech_token.value, mech_token.length);
        gss_release_buffer(&tmpmin, &mech_token);
    }

    if (k5_buf_data(&buf) == NULL) {
        *minor_status = ENOMEM;
        return GSS_S_FAILURE;
    }
    return k5buf_to_gss(minor_status, &buf, token);

error:
    k5_free_buf(&buf);
    return status;
}
