/* Copyright (c) 2010, Jean-Yves Avenard and Hydrix Pty Ltd
 * 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.
 *  * Neither the name of Hydrix nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * 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 OWNER 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 <windows.h>
#include <stdio.h>

#include "gssapi.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* Reserved static storage for GSS_oids.  Comments are quotes from RFC 2744. */
#define oids ((gss_OID_desc *)const_oids)
static const gss_OID_desc const_oids[] = {
    /*
     * The implementation must reserve static storage for a
	 * gss_OID_desc object containing the value */
    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},
    /* corresponding to an object-identifier value of
	 * {iso(1) member-body(2) United States(840) mit(113554)
	 * infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant
	 * GSS_C_NT_USER_NAME should be initialized to point
	 * to that gss_OID_desc.
	 */

    /*
	 * The implementation must reserve static storage for a
	 * gss_OID_desc object containing the value */
    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},
    /* corresponding to an object-identifier value of
	 * {iso(1) member-body(2) United States(840) mit(113554)
	 * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
	 * The constant GSS_C_NT_MACHINE_UID_NAME should be
	 * initialized to point to that gss_OID_desc.
	 */

    /*
    * The implementation must reserve static storage for a
    * gss_OID_desc object containing the value */
    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},
    /* corresponding to an object-identifier value of
    * {iso(1) member-body(2) United States(840) mit(113554)
    * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
    * The constant GSS_C_NT_STRING_UID_NAME should be
    * initialized to point to that gss_OID_desc.
    */

    /*
     * The implementation must reserve static storage for a
     * gss_OID_desc object containing the value */
    {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
    /* corresponding to an object-identifier value of
     * {iso(1) org(3) dod(6) internet(1) security(5)
     * nametypes(6) gss-host-based-services(2)).  The constant
     * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
     * to that gss_OID_desc.  This is a deprecated OID value, and
     * implementations wishing to support hostbased-service names
     * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
     * defined below, to identify such names;
     * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
     * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
     * parameter, but should not be emitted by GSS-API
     * implementations
     */

    /*
     * The implementation must reserve static storage for a
     * gss_OID_desc object containing the value */
    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"},
    /* corresponding to an object-identifier value of
     * {iso(1) member-body(2) Unites States(840) mit(113554)
     * infosys(1) gssapi(2) generic(1) service_name(4)}.
     * The constant GSS_C_NT_HOSTBASED_SERVICE should be
     * initialized to point to that gss_OID_desc.
     */

    /*
     * The implementation must reserve static storage for a
     * gss_OID_desc object containing the value */
    {6, (void *)"\x2b\x06\01\x05\x06\x03"},
    /* corresponding to an object identifier value of
     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
     * 6(nametypes), 3(gss-anonymous-name)}.  The constant
     * and GSS_C_NT_ANONYMOUS should be initialized to point
     * to that gss_OID_desc.
     */

    /*
     * The implementation must reserve static storage for a
     * gss_OID_desc object containing the value */
    {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
    /* corresponding to an object-identifier value of
     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
     * 6(nametypes), 4(gss-api-exported-name)}.  The constant
     * GSS_C_NT_EXPORT_NAME should be initialized to point
     * to that gss_OID_desc.
     */
};

/* Here are the constants which point to the static structure above.
 *
 * Constants of the form GSS_C_NT_* are specified by rfc 2744.
 *
 * Constants of the form gss_nt_* are the original MIT krb5 names
 * found in gssapi_generic.h.  They are provided for compatibility. */

gss_OID GSS_C_NT_USER_NAME           = oids+0;
gss_OID gss_nt_user_name             = oids+0;

gss_OID GSS_C_NT_MACHINE_UID_NAME    = oids+1;
gss_OID gss_nt_machine_uid_name      = oids+1;

gss_OID GSS_C_NT_STRING_UID_NAME     = oids+2;
gss_OID gss_nt_string_uid_name       = oids+2;

gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3;
gss_OID gss_nt_service_name_v2       = oids+3;

gss_OID GSS_C_NT_HOSTBASED_SERVICE   = oids+4;
gss_OID gss_nt_service_name          = oids+4;

gss_OID GSS_C_NT_ANONYMOUS           = oids+5;

gss_OID GSS_C_NT_EXPORT_NAME         = oids+6;
gss_OID gss_nt_exported_name         = oids+6;

#ifdef _WIN64
#define GSSAPI_DLL "gssapi64.dll"
#else
#define GSSAPI_DLL "gssapi32.dll"
#endif

static HINSTANCE hinstLib = LoadLibrary(TEXT(GSSAPI_DLL)); // Will let Windows automatically call FreeLibrary. This is for speed-sake

#define CALL_ENTRY(x, type, ...) { \
    if (hinstLib != NULL) \
    { \
        ProcAdd = (type)(GetProcAddress(hinstLib, TEXT(x))); \
        if (NULL != ProcAdd) \
        { \
            return (ProcAdd) (__VA_ARGS__); \
        } \
    } \
    *a1 = 0; \
    return GSS_S_FAILURE; \
}

OM_uint32 gss_display_status
(OM_uint32 *a1,		/* minor_status */
            OM_uint32 a2,			/* status_value */
            int a3,			/* status_type */
            gss_OID a4,			/* mech_type (used to be const) */
            OM_uint32 *a5,		/* message_context */
            gss_buffer_t a6		/* status_string */
           )
{
    typedef OM_uint32 (WINAPI *pgss_display_status)(OM_uint32 *, OM_uint32 , int, gss_OID, OM_uint32 *, gss_buffer_t );
    pgss_display_status ProcAdd;

    CALL_ENTRY("gss_display_status", pgss_display_status, a1, a2, a3, a4, a5, a6);
}

OM_uint32 gss_release_buffer
(OM_uint32 *a1,		/* minor_status */
            gss_buffer_t a2		/* buffer */
           )
{
    typedef OM_uint32 (WINAPI *pgss_release_buffer)(OM_uint32 *, gss_buffer_t);
    pgss_release_buffer ProcAdd;

    CALL_ENTRY("gss_release_buffer", pgss_release_buffer, a1, a2);
}

OM_uint32 gss_wrap
(OM_uint32 *a1,		/* minor_status */
	    gss_ctx_id_t a2,		/* context_handle */
	    int a3,			/* conf_req_flag */
	    gss_qop_t a4,			/* qop_req */
	    gss_buffer_t a5,		/* input_message_buffer */
	    int *a6,			/* conf_state */
	    gss_buffer_t a7		/* output_message_buffer */
	   )
{
    typedef OM_uint32 (WINAPI *pgss_wrap)(OM_uint32 *, gss_ctx_id_t, int, gss_qop_t, gss_buffer_t, int *, gss_buffer_t);
    pgss_wrap ProcAdd;

    CALL_ENTRY("gss_wrap", pgss_wrap, a1, a2, a3, a4, a5, a6, a7);
}

OM_uint32 gss_unwrap
(OM_uint32 *a1,		/* minor_status */
	    gss_ctx_id_t a2,		/* context_handle */
	    gss_buffer_t a3,		/* input_message_buffer */
	    gss_buffer_t a4,		/* output_message_buffer */
	    int * a5,			/* conf_state */
	    gss_qop_t *a6		/* qop_state */
	   )
{
    typedef OM_uint32 (WINAPI *pgss_unwrap)(OM_uint32 *, gss_ctx_id_t , gss_buffer_t, gss_buffer_t, int *, gss_qop_t *);
    pgss_unwrap ProcAdd;

    CALL_ENTRY("gss_unwrap", pgss_unwrap, a1, a2, a3, a4, a5, a6);
}

OM_uint32 gss_delete_sec_context
(OM_uint32 *a1,		/* minor_status */
            gss_ctx_id_t *a2,		/* context_handle */
            gss_buffer_t a3		/* output_token */
           )
{
    typedef OM_uint32 (WINAPI *pgss_delete_sec_context)(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t);
    pgss_delete_sec_context ProcAdd;

    CALL_ENTRY("gss_delete_sec_context", pgss_delete_sec_context, a1, a2, a3);
}

OM_uint32 gss_release_name
(OM_uint32 *a1,		/* minor_status */
            gss_name_t *a2		/* input_name */
           )
{
    typedef OM_uint32 (WINAPI *pgss_release_name)(OM_uint32 *, gss_name_t *);
    pgss_release_name ProcAdd;

    CALL_ENTRY("gss_release_name", pgss_release_name, a1, a2);
}

OM_uint32 gss_release_cred
(OM_uint32 *a1,		/* minor_status */
            gss_cred_id_t *a2		/* cred_handle */
           )
{
    typedef OM_uint32 (WINAPI *pgss_release_cred)(OM_uint32 *, gss_cred_id_t *);
    pgss_release_cred ProcAdd;

    CALL_ENTRY("gss_release_cred", pgss_release_cred, a1, a2);
}

OM_uint32 gss_import_name
(OM_uint32 *a1,		/* minor_status */
            gss_buffer_t a2,		/* input_name_buffer */
            gss_OID a3,			/* input_name_type(used to be const) */
            gss_name_t *a4		/* output_name */
           )
{
    typedef OM_uint32 (WINAPI *pgss_import_name)(OM_uint32 *, gss_buffer_t , gss_OID, gss_name_t *);
    pgss_import_name ProcAdd;

    CALL_ENTRY("gss_import_name", pgss_import_name, a1, a2, a3, a4);
}

OM_uint32 gss_acquire_cred
(OM_uint32 *a1,		/* minor_status */
            gss_name_t a2,			/* desired_name */
            OM_uint32 a3,			/* time_req */
            gss_OID_set a4,		/* desired_mechs */
            gss_cred_usage_t a5,		/* cred_usage */
            gss_cred_id_t *a6,	/* output_cred_handle */
            gss_OID_set *a7,		/* actual_mechs */
            OM_uint32 *a8		/* time_rec */
           )
{
    typedef OM_uint32 (WINAPI *pgss_acquire_cred)(OM_uint32 *, gss_name_t , OM_uint32, gss_OID_set, gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *, OM_uint32 *);
    pgss_acquire_cred ProcAdd;

    CALL_ENTRY("gss_acquire_cred", pgss_acquire_cred, a1, a2, a3, a4, a5, a6, a7, a8);
}

OM_uint32 gss_accept_sec_context
(OM_uint32 *a1,		/* minor_status */
            gss_ctx_id_t *a2,		/* context_handle */
            gss_cred_id_t a3,		/* acceptor_cred_handle */
            gss_buffer_t a4,		/* input_token_buffer */
            gss_channel_bindings_t a5,	/* input_chan_bindings */
            gss_name_t *a6,		/* src_name */
            gss_OID *a7,		/* mech_type */
            gss_buffer_t a8,		/* output_token */
            OM_uint32 *a9,		/* ret_flags */
            OM_uint32 *a10,		/* time_rec */
            gss_cred_id_t *a11		/* delegated_cred_handle */
           )
{
    typedef OM_uint32 (WINAPI *pgss_accept_sec_context)(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t, gss_buffer_t, gss_channel_bindings_t, gss_name_t *, gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *, gss_cred_id_t *);
    pgss_accept_sec_context ProcAdd;

    CALL_ENTRY("gss_accept_sec_context", pgss_accept_sec_context, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
}

OM_uint32 gss_compare_name
(OM_uint32 *a1,		/* minor_status */
            gss_name_t a2,			/* name1 */
            gss_name_t a3,			/* name2 */
            int *a4			/* name_equal */
           )
{
    typedef OM_uint32 (WINAPI *pgss_compare_name)(OM_uint32 *, gss_name_t , gss_name_t, int *);
    pgss_compare_name ProcAdd;

    CALL_ENTRY("gss_compare_name", pgss_compare_name, a1, a2, a3, a4);
}

OM_uint32 gss_wrap_size_limit
(OM_uint32 *a1,		/* minor_status */
	    gss_ctx_id_t a2,		/* context_handle */
	    int a3,			/* conf_req_flag */
	    gss_qop_t a4,			/* qop_req */
	    OM_uint32 a5,			/* req_output_size */
	    OM_uint32 *a6			/* max_input_size */
	   )
{
    typedef OM_uint32 (WINAPI *pgss_wrap_size_limit)(OM_uint32 *, gss_ctx_id_t, int, gss_qop_t, OM_uint32, OM_uint32 *);
    pgss_wrap_size_limit ProcAdd;

    CALL_ENTRY("gss_wrap_size_limit", pgss_wrap_size_limit, a1, a2, a3, a4, a5, a6);
}

OM_uint32 gss_init_sec_context
(OM_uint32 *a1,		/* minor_status */
            gss_cred_id_t a2,		/* claimant_cred_handle */
            gss_ctx_id_t *a3,		/* context_handle */
            gss_name_t a4,			/* target_name */
            gss_OID a5,			/* mech_type (used to be const) */
            OM_uint32 a6,			/* req_flags */
            OM_uint32 a7,			/* time_req */
            gss_channel_bindings_t a8,	/* input_chan_bindings */
            gss_buffer_t a9,		/* input_token */
            gss_OID *a10,		/* actual_mech_type */
            gss_buffer_t a11,		/* output_token */
            OM_uint32 *a12,		/* ret_flags */
            OM_uint32 *a13		/* time_rec */
           )
{
    typedef OM_uint32 (WINAPI *pgss_init_sec_context)(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *, gss_name_t, gss_OID, OM_uint32, OM_uint32, gss_channel_bindings_t, gss_buffer_t, gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *);
    pgss_init_sec_context ProcAdd;

    CALL_ENTRY("gss_init_sec_context", pgss_init_sec_context, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
}

OM_uint32 gss_inquire_context
(OM_uint32 *a1,		/* minor_status */
	    gss_ctx_id_t a2,		/* context_handle */
	    gss_name_t *a3,		/* src_name */
	    gss_name_t *a4,		/* targ_name */
	    OM_uint32 *a5,		/* lifetime_rec */
	    gss_OID *a6,		/* mech_type */
	    OM_uint32 *a7,		/* ctx_flags */
	    int *a8,           	/* locally_initiated */
	    int *a9			/* open */
	   )
{
    typedef OM_uint32 (WINAPI *pgss_inquire_context)(OM_uint32 *, gss_ctx_id_t, gss_name_t *, gss_name_t *, OM_uint32 *, gss_OID *, OM_uint32 *, int *, int *);
    pgss_inquire_context ProcAdd;

    CALL_ENTRY("gss_inquire_context", pgss_inquire_context, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}

OM_uint32 gss_display_name
(OM_uint32 *a1,		/* minor_status */
            gss_name_t a2,			/* input_name */
            gss_buffer_t a3,		/* output_name_buffer */
            gss_OID *a4		/* output_name_type */
           )
{
    typedef OM_uint32 (WINAPI *pgss_display_name)(OM_uint32 *, gss_name_t, gss_buffer_t, gss_OID *);
    pgss_display_name ProcAdd;

    CALL_ENTRY("gss_display_name", pgss_display_name, a1, a2, a3, a4);
}

#ifdef __cplusplus
}
#endif
