<commit_before>package gss

/*
#cgo pkg-config: krb5-gssapi
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>
#include <gssapi/gssapi_krb5.h>
#include <gssapi/gssapi_ext.h>

static gss_OID_desc nth_oid_in_set(gss_OID_set_desc *oset, unsigned int n)
{
	return oset->elements[n];
}
static gss_buffer_desc nth_buffer_in_set(gss_buffer_set_desc *bset, unsigned int n)
{
	return bset->elements[n];
}
static gss_key_value_element_desc *alloc_n_kvset_elems(unsigned int n)
{
	return malloc(sizeof(gss_key_value_element_desc));
}
static void kv_set(gss_key_value_set_desc *kvset, int i, char *key, char *value)
{
	kvset->elements[i].key = key;
	kvset->elements[i].value = value;
}
static void free_kv_set(gss_key_value_set_desc kvset)
{
	unsigned i;
	for (i = 0; i < kvset.count; i++) {
		free((char *) kvset.elements[i].key);
		free((char *) kvset.elements[i].value);
	}
	free(kvset.elements);
}
static void free_gss_buffer(gss_buffer_desc buffer)
{
	free(buffer.value);
}
static void free_oid(gss_OID_desc oid)
{
	free(oid.elements);
}
static void free_oid_set(gss_OID_set buffer)
{
	OM_uint32 minor;
	gss_release_oid_set(&minor, &buffer);
}
static void *
copyOid(unsigned char *bytes, int len)
{
	void *ret;

	if (len < 0) {
		return NULL;
	}
	ret = malloc(len);
	if (ret != NULL) {
		memcpy(ret, bytes, len);
	}
	return ret;
}
*/
import "C"
import "unsafe"
import "encoding/asn1"
import "fmt"

const (
	C_DCE_STYLE = C.GSS_C_DCE_STYLE
	C_IDENTIFY_FLAG = C.GSS_C_IDENTIFY_FLAG
	C_EXTENDED_ERROR_FLAG = C.GSS_C_EXTENDED_ERROR_FLAG

//	C_NO_IOV_BUFFER = C.GSS_C_NO_IOV_BUFFER

	IOV_BUFFER_TYPE_EMPTY = C.GSS_IOV_BUFFER_TYPE_EMPTY
	IOV_BUFFER_TYPE_DATA = C.GSS_IOV_BUFFER_TYPE_DATA
	IOV_BUFFER_TYPE_HEADER = C.GSS_IOV_BUFFER_TYPE_HEADER
	IOV_BUFFER_TYPE_MECH_PARAMS = C.GSS_IOV_BUFFER_TYPE_MECH_PARAMS
	IOV_BUFFER_TYPE_TRAILER = C.GSS_IOV_BUFFER_TYPE_TRAILER
	IOV_BUFFER_TYPE_PADDING = C.GSS_IOV_BUFFER_TYPE_PADDING
	IOV_BUFFER_TYPE_STREAM = C.GSS_IOV_BUFFER_TYPE_STREAM
	IOV_BUFFER_TYPE_SIGN_ONLY = C.GSS_IOV_BUFFER_TYPE_SIGN_ONLY
	IOV_BUFFER_TYPE_MIC_TOKEN = C.GSS_IOV_BUFFER_TYPE_MIC_TOKEN
	IOV_BUFFER_FLAG_MASK = C.GSS_IOV_BUFFER_FLAG_MASK
	IOV_BUFFER_FLAG_ALLOCATE = C.GSS_IOV_BUFFER_FLAG_ALLOCATE
	IOV_BUFFER_FLAG_ALLOCATED = C.GSS_IOV_BUFFER_FLAG_ALLOCATED

//	C_NO_CRED_STORE

	C_BOTH = C.GSS_C_BOTH
	C_INITIATE = C.GSS_C_INITIATE
	C_ACCEPT = C.GSS_C_ACCEPT

	C_GSS_CODE = C.GSS_C_GSS_CODE
	C_MECH_CODE = C.GSS_C_MECH_CODE

	C_AF_UNSPEC = C.GSS_C_AF_UNSPEC
	C_AF_LOCAL = C.GSS_C_AF_LOCAL
	C_AF_INET = C.GSS_C_AF_INET
	C_AF_IMPLINK = C.GSS_C_AF_IMPLINK
	C_AF_PUP = C.GSS_C_AF_PUP
	C_AF_CHAOS = C.GSS_C_AF_CHAOS
	C_AF_NS = C.GSS_C_AF_NS
	C_AF_NBS = C.GSS_C_AF_NBS
	C_AF_ECMA = C.GSS_C_AF_ECMA
	C_AF_DATAKIT = C.GSS_C_AF_DATAKIT
	C_AF_CCITT = C.GSS_C_AF_CCITT
	C_AF_SNA = C.GSS_C_AF_SNA
	C_AF_DECnet = C.GSS_C_AF_DECnet
	C_AF_DLI = C.GSS_C_AF_DLI
	C_AF_LAT = C.GSS_C_AF_LAT
	C_AF_HYLINK = C.GSS_C_AF_HYLINK
	C_AF_APPLETALK = C.GSS_C_AF_APPLETALK
	C_AF_BSC = C.GSS_C_AF_BSC
	C_AF_DSS = C.GSS_C_AF_DSS
	C_AF_OSI = C.GSS_C_AF_OSI
	C_AF_NETBIOS = C.GSS_C_AF_NETBIOS
	C_AF_X25 = C.GSS_C_AF_X25
	C_AF_NULLADDR = C.GSS_C_AF_NULLADDR

//	C_NO_NAME = nil
//	C_NO_BUFFER = nil
//	C_NO_OID = nil
//	C_NO_OID_SET = nil
//	C_NO_CONTEXT = nil
//	C_NO_CREDENTIAL = nil
//	C_NO_CHANNEL_BINDINGS = nil
//	C_EMPTY_BUFFER = make([]byte, 0)

//	C_NULL_OID = nil
//	C_NULL_OID_SET = nil

	C_QOP_DEFAULT = C.GSS_C_QOP_DEFAULT
	C_INDEFINITE = C.GSS_C_INDEFINITE

	S_COMPLETE = C.GSS_S_COMPLETE
	C_CALLING_ERROR_OFFSET = C.GSS_C_CALLING_ERROR_OFFSET
	C_ROUTINE_ERROR_OFFSET = C.GSS_C_ROUTINE_ERROR_OFFSET
	C_SUPPLEMENTARY_OFFSET = C.GSS_C_SUPPLEMENTARY_OFFSET
	C_CALLING_ERROR_MASK = C.GSS_C_CALLING_ERROR_MASK
	C_ROUTINE_ERROR_MASK = C.GSS_C_ROUTINE_ERROR_MASK
	C_SUPPLEMENTARY_MASK = C.GSS_C_SUPPLEMENTARY_MASK
	S_CALL_INACCESSIBLE_READ = C.GSS_S_CALL_INACCESSIBLE_READ
	S_CALL_INACCESSIBLE_WRITE = C.GSS_S_CALL_INACCESSIBLE_WRITE
	S_CALL_BAD_STRUCTURE = C.GSS_S_CALL_BAD_STRUCTURE
	S_BAD_MECH = C.GSS_S_BAD_MECH
	S_BAD_NAME = C.GSS_S_BAD_NAME
	S_BAD_NAMETYPE = C.GSS_S_BAD_NAMETYPE
	S_BAD_BINDINGS = C.GSS_S_BAD_BINDINGS
	S_BAD_STATUS = C.GSS_S_BAD_STATUS
	S_BAD_SIG = C.GSS_S_BAD_SIG
	S_NO_CRED = C.GSS_S_NO_CRED
	S_NO_CONTEXT = C.GSS_S_NO_CONTEXT
	S_DEFECTIVE_TOKEN = C.GSS_S_DEFECTIVE_TOKEN
	S_DEFECTIVE_CREDENTIAL = C.GSS_S_DEFECTIVE_CREDENTIAL
	S_CREDENTIALS_EXPIRED = C.GSS_S_CREDENTIALS_EXPIRED
	S_CONTEXT_EXPIRED = C.GSS_S_CONTEXT_EXPIRED
	S_FAILURE = C.GSS_S_FAILURE
	S_BAD_QOP = C.GSS_S_BAD_QOP
	S_UNAUTHORIZED = C.GSS_S_UNAUTHORIZED
	S_UNAVAILABLE = C.GSS_S_UNAVAILABLE
	S_DUPLICATE_ELEMENT = C.GSS_S_DUPLICATE_ELEMENT
	S_NAME_NOT_MN = C.GSS_S_NAME_NOT_MN
	S_BAD_MECH_ATTR = C.GSS_S_BAD_MECH_ATTR
	S_CONTINUE_NEEDED = C.GSS_S_CONTINUE_NEEDED
	S_DUPLICATE_TOKEN = C.GSS_S_DUPLICATE_TOKEN
	S_OLD_TOKEN = C.GSS_S_OLD_TOKEN
	S_UNSEQ_TOKEN = C.GSS_S_UNSEQ_TOKEN
	S_GAP_TOKEN = C.GSS_S_GAP_TOKEN
	C_PRF_KEY_FULL = C.GSS_C_PRF_KEY_FULL
	C_PRF_KEY_PARTIAL = C.GSS_C_PRF_KEY_PARTIAL
	S_CRED_UNAVAIL = C.GSS_S_CRED_UNAVAIL
)

var (
	KRB5_NT_HOSTBASED_SERVICE_NAME = coidToOid(*C.GSS_KRB5_NT_HOSTBASED_SERVICE_NAME)
	KRB5_NT_USER_NAME = coidToOid(*C.GSS_KRB5_NT_USER_NAME)
	KRB5_NT_MACHINE_UID_NAME = coidToOid(*C.GSS_KRB5_NT_MACHINE_UID_NAME)
	KRB5_NT_STRING_UID_NAME = coidToOid(*C.GSS_KRB5_NT_STRING_UID_NAME)

	C_INQ_SSPI_SESSION_KEY = coidToOid(*C.GSS_C_INQ_SSPI_SESSION_KEY)
	C_ATTR_LOCAL_LOGIN_USER = bufferToBytes(*C.GSS_C_ATTR_LOCAL_LOGIN_USER)
	C_NT_COMPOSITE_EXPORT = coidToOid(*C.GSS_C_NT_COMPOSITE_EXPORT)

	C_NT_USER_NAME = coidToOid(*C.GSS_C_NT_USER_NAME)
	C_NT_MACHINE_UID_NAME = coidToOid(*C.GSS_C_NT_MACHINE_UID_NAME)
	C_NT_STRING_UID_NAME = coidToOid(*C.GSS_C_NT_STRING_UID_NAME)
	C_NT_HOSTBASED_SERVICE_X = coidToOid(*C.GSS_C_NT_HOSTBASED_SERVICE_X)
	C_NT_HOSTBASED_SERVICE = coidToOid(*C.GSS_C_NT_HOSTBASED_SERVICE)
	C_NT_ANONYMOUS = coidToOid(*C.GSS_C_NT_ANONYMOUS)
	C_NT_EXPORT_NAME = coidToOid(*C.GSS_C_NT_EXPORT_NAME)

	C_MA_MECH_CONCRETE = coidToOid(*C.GSS_C_MA_MECH_CONCRETE)
	C_MA_MECH_PSEUDO = coidToOid(*C.GSS_C_MA_MECH_PSEUDO)
	C_MA_MECH_COMPOSITE = coidToOid(*C.GSS_C_MA_MECH_COMPOSITE)
	C_MA_MECH_NEGO = coidToOid(*C.GSS_C_MA_MECH_NEGO)
	C_MA_MECH_GLUE = coidToOid(*C.GSS_C_MA_MECH_GLUE)
	C_MA_NOT_MECH = coidToOid(*C.GSS_C_MA_NOT_MECH)
	C_MA_DEPRECATED = coidToOid(*C.GSS_C_MA_DEPRECATED)
	C_MA_NOT_DFLT_MECH = coidToOid(*C.GSS_C_MA_NOT_DFLT_MECH)
	C_MA_ITOK_FRAMED = coidToOid(*C.GSS_C_MA_ITOK_FRAMED)
	C_MA_AUTH_INIT = coidToOid(*C.GSS_C_MA_AUTH_INIT)
	C_MA_AUTH_TARG = coidToOid(*C.GSS_C_MA_AUTH_TARG)
	C_MA_AUTH_INIT_INIT = coidToOid(*C.GSS_C_MA_AUTH_INIT_INIT)
	C_MA_AUTH_TARG_INIT = coidToOid(*C.GSS_C_MA_AUTH_TARG_INIT)
	C_MA_AUTH_INIT_ANON = coidToOid(*C.GSS_C_MA_AUTH_INIT_ANON)
	C_MA_AUTH_TARG_ANON = coidToOid(*C.GSS_C_MA_AUTH_TARG_ANON)
	C_MA_DELEG_CRED = coidToOid(*C.GSS_C_MA_DELEG_CRED)
	C_MA_INTEG_PROT = coidToOid(*C.GSS_C_MA_INTEG_PROT)
	C_MA_CONF_PROT = coidToOid(*C.GSS_C_MA_CONF_PROT)
	C_MA_MIC = coidToOid(*C.GSS_C_MA_MIC)
	C_MA_WRAP = coidToOid(*C.GSS_C_MA_WRAP)
	C_MA_PROT_READY = coidToOid(*C.GSS_C_MA_PROT_READY)
	C_MA_REPLAY_DET = coidToOid(*C.GSS_C_MA_REPLAY_DET)
	C_MA_OOS_DET = coidToOid(*C.GSS_C_MA_OOS_DET)
	C_MA_CBINDINGS = coidToOid(*C.GSS_C_MA_CBINDINGS)
	C_MA_PFS = coidToOid(*C.GSS_C_MA_PFS)
	C_MA_COMPRESS = coidToOid(*C.GSS_C_MA_COMPRESS)
	C_MA_CTX_TRANS = coidToOid(*C.GSS_C_MA_CTX_TRANS)

	KRB5_NT_PRINCIPAL_NAME = coidToOid(*C.GSS_KRB5_NT_PRINCIPAL_NAME)

	Mech_krb5 = coidToOid(*C.gss_mech_krb5)
	Mech_krb5_old = coidToOid(*C.gss_mech_krb5_old)
	Mech_krb5_wrong = coidToOid(*C.gss_mech_krb5_wrong)
	Mech_iakerb = coidToOid(*C.gss_mech_iakerb)
	Mech_set_krb5 = coidSetToOids(C.gss_mech_set_krb5)
	Mech_set_krb5_old = coidSetToOids(C.gss_mech_set_krb5_old)
	Mech_set_krb5_both = coidSetToOids(C.gss_mech_set_krb5_both)

	NT_krb5_name = coidToOid(*C.gss_nt_krb5_name)
	NT_krb5_principal = coidToOid(*C.gss_nt_krb5_principal)
	//Krb5_gss_oid_array = coidToOid(C.krb5_gss_oid_array)
)

type CredHandle C.gss_cred_id_t

type ContextHandle C.gss_ctx_id_t

type InternalName C.gss_name_t

type ChannelBindings struct {
	initiatorAddressType, acceptorAddressType uint32
	initiatorAddress, acceptorAddress, applicationData []byte
}

type Flags struct {
	deleg, delegPolicy, mutual, replay, sequence, anon, conf, integ bool
}

type IOV struct {
	data []byte
}

/* bytesToBuffer populates a gss_buffer_t with a borrowed reference to the
 * contents of the slice. */
func bytesToBuffer(data []byte) (cdesc C.gss_buffer_desc) {
	value := unsafe.Pointer(&data[0])
	length := C.size_t(len(data))

	cdesc.value = value
	cdesc.length = length
	return
}

/* bufferToBytes creates a byte array using the contents of the passed-in
 * buffer. */
func bufferToBytes(cdesc C.gss_buffer_desc) (b []byte) {
	length := C.int(cdesc.length)

	b = C.GoBytes(cdesc.value, length)
	return
}

/* buffersToBytes creates a byte array using the contents of the passed-in
 * buffer. */
func buffersToBytes(cdesc C.gss_buffer_set_desc) (b [][]byte) {
	count := uint(cdesc.count)
	var i uint

	b = make([][]byte, count)
	for i = 0; i < count; i++ {
		b[i] = bufferToBytes(C.nth_buffer_in_set(&cdesc, C.uint(i)))
	}
	return
}

/* Encode a tag and a length as a DER definite length */
func makeTagAndLength(tag, length int) (l []byte) {
	var count, bits int

	if length <= 127 {
		l = make([]byte, 2)
		l[0] = byte(tag)
		l[1] = byte(length)
		return
	}
	count = 0
	bits = length
	for bits != 0 {
		count++
		bits = bits >> 8
	}
	if count > 126 {
		return nil
	}
	l = make([]byte, 2 + count)
	count = 0
	bits = length
	l[0] = byte(tag)
	for bits != 0 {
		l[len(l) - 1 - count] = byte(bits & 0xff)
		count++
		bits = bits >> 8
	}
	l[1] = byte((count | 0x80) & 0x7f)
	return
}

/* Split up a DER item */
func splitTagAndLength(tlv []byte) (class int, constructed bool, tag, length int, value []byte) {
	tbytes := 1
	lbytes := 1

	class = int((tlv[0] & 0xc0) >> 6)
	constructed = (tlv[0] & 0x20) != 0
	tag = int(tlv[0] & 0x1f)
	if tag == 0x1f {
		tag = 0
		for tlv[tbytes] & 0x80 != 0 {
			tag = (tag << 7) + int(tlv[tbytes] & 0x7f)
			tbytes++
		}
		tag = (tag << 7) + int(tlv[tbytes] & 0x7f)
		tbytes++
	}
	if tlv[tbytes] & 0x80 == 0 {
		length = int(tlv[tbytes] & 0x7f)
	} else {
		lbytes = int(tlv[tbytes] & 0x7f)
		if lbytes == 0 {
			value = nil
			return
		}
		for count := 0; count < lbytes; count++ {
			length = (length << 8) + int(tlv[tbytes + count] & 0xff)
		}
	}
	if len(tlv) != tbytes + lbytes + length {
		value = nil
		return
	}
	value = tlv[(tbytes + lbytes):]
	return
}

/* coidToOid produces an asn1.ObjectIdentifier from the library's preferred
 * bytes-and-length representation, which is just the DER encoding without a
 * tag and length. */
func coidToOid(coid C.gss_OID_desc) (oid asn1.ObjectIdentifier) {
	length := C.int(coid.length)
	b := C.GoBytes(coid.elements, length)

	b = append(makeTagAndLength(6, len(b)), b...)
	asn1.Unmarshal(b, &oid)
	return
}

/* oidToCOid converts an asn1.ObjectIdentifier into an array of encoded bytes
 * without the tag and length, which is how the C library expects them to be
 * structured. */
func oidToCOid(oid asn1.ObjectIdentifier) (coid C.gss_OID_desc) {
	b, _ := asn1.Marshal(oid)
	if b == nil {
		return
	}
	_, _, _, _, v := splitTagAndLength(b)
	if v == nil {
		return
	}
	length := len(v)
	coid.elements = C.copyOid((*C.uchar)(&v[0]), C.int(length))
	coid.length = C.OM_uint32(length)
	return
}

/* oidsToCOidSet converts an array of asn1.ObjectIdentifier items into an array
 * of arrays of encoded bytes-and-lengths, which is how the C library expects
 * them to be structured. */
func oidsToCOidSet(oidSet []asn1.ObjectIdentifier) (coids C.gss_OID_set) {
	var major, minor C.OM_uint32
	if oidSet == nil {
		return
	}
	major = C.gss_create_empty_oid_set(&minor, &coids)
	if major != 0 {
		return
	}

	for _, o := range oidSet {
		oid := oidToCOid(o)
		major = C.gss_add_oid_set_member(&minor, &oid, &coids)
		C.free_oid(oid)
		if major != 0 {
			major = C.gss_release_oid_set(&minor, &coids)
			coids = nil
			return
		}
	}
	return
}

/* coidSetToOids produces an array of asn1.ObjectIdentifier items from the
 * library's preferred array-of-bytes-and-lengths representation. */
func coidSetToOids(coids *C.gss_OID_set_desc) (oidSet []asn1.ObjectIdentifier) {
	if coids == nil {
		return nil
	}

	oidSet = make([]asn1.ObjectIdentifier, coids.count)
	if oidSet == nil {
		return
	}

	for o := 0; o < int(coids.count); o++ {
		coid := C.nth_oid_in_set(coids, C.uint(o))
		oidSet[o] = coidToOid(coid)
	}

	return
}

func bindingsToCBindings(bindings *ChannelBindings) (cbindings C.gss_channel_bindings_t) {
	if bindings == nil {
		return nil
	}
	cbindings.initiator_addrtype = C.OM_uint32(bindings.initiatorAddressType)
	cbindings.initiator_address = bytesToBuffer(bindings.initiatorAddress)
	cbindings.acceptor_addrtype = C.OM_uint32(bindings.acceptorAddressType)
	cbindings.acceptor_address = bytesToBuffer(bindings.acceptorAddress)
	cbindings.application_data = bytesToBuffer(bindings.applicationData)
	return
}

func cbindingsToBindings(cbindings C.gss_channel_bindings_t) (bindings *ChannelBindings) {
	if cbindings == nil {
		return nil
	}
	bindings.initiatorAddressType = uint32(cbindings.initiator_addrtype)
	bindings.initiatorAddress = bufferToBytes(cbindings.initiator_address)
	bindings.acceptorAddressType = uint32(cbindings.acceptor_addrtype)
	bindings.acceptorAddress = bufferToBytes(cbindings.acceptor_address)
	bindings.applicationData = bufferToBytes(cbindings.application_data)
	return
}

func credStoreToKVSet(credStore [][2]string) (kvset C.gss_key_value_set_desc) {
	kvset.elements = C.alloc_n_kvset_elems(C.uint(len(credStore)))
	if kvset.elements == nil {
		return
	}
	for i, kv := range credStore {
		C.kv_set(&kvset, C.int(i), C.CString(kv[0]), C.CString(kv[1]))
	}
	kvset.count = C.OM_uint32(len(credStore))
	return
}

func AcquireCred(desiredName InternalName, lifetimeReq uint32, desiredMechs []asn1.ObjectIdentifier, credUsage uint32) (majorStatus, minorStatus uint32, outputCredHandle CredHandle, actualMechs []asn1.ObjectIdentifier, lifetimeRec uint32) {
	name := C.gss_name_t(desiredName)
	lifetime := C.OM_uint32(lifetimeReq)
	usage := C.gss_cred_usage_t(credUsage)
	var major, minor C.OM_uint32
	var desired, actual C.gss_OID_set
	var handle C.gss_cred_id_t

	desired = oidsToCOidSet(desiredMechs)
	major = C.gss_acquire_cred(&minor, name, lifetime, desired, usage, &handle, &actual, &lifetime)
	C.free_oid_set(desired)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	outputCredHandle = CredHandle(handle)
	actualMechs = coidSetToOids(actual)
	C.free_oid_set(actual)
	lifetimeRec = uint32(lifetime)
	return
}

func ReleaseCred(credHandle CredHandle) (majorStatus, minorStatus uint32) {
	handle := C.gss_cred_id_t(credHandle)
	var major, minor C.OM_uint32

	major = C.gss_release_cred(&minor, &handle)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	return
}

func InquireCred(credHandle CredHandle) (majorStatus, minorStatus uint32, credName InternalName, lifetimeRec, credUsage uint32, mechSet []asn1.ObjectIdentifier) {
	handle := C.gss_cred_id_t(credHandle)
	name := C.gss_name_t(nil)
	var major, minor, lifetime C.OM_uint32
	var usage C.gss_cred_usage_t
	var mechs C.gss_OID_set

	major = C.gss_inquire_cred(&minor, handle, &name, &lifetime, &usage, &mechs)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	credName = InternalName(name)
	lifetimeRec = uint32(lifetime)
	credUsage = uint32(usage)
	mechSet = coidSetToOids(mechs)
	C.free_oid_set(mechs)
	return
}

func AddCred(credHandle CredHandle, desiredName InternalName, desiredMech asn1.ObjectIdentifier, initiatorTimeReq, acceptorTimeReq, credUsage uint32, outputCredHandle CredHandle) (majorStatus, minorStatus uint32, outputCredHandleRec CredHandle, actualMechs []asn1.ObjectIdentifier, initiatorTimeRec, acceptorTimeRec uint32) {
	handle := C.gss_cred_id_t(credHandle)
	name := C.gss_name_t(desiredName)
	mech := oidToCOid(desiredMech)
	itime := C.OM_uint32(initiatorTimeReq)
	atime := C.OM_uint32(acceptorTimeReq)
	usage := C.gss_cred_usage_t(credUsage)
	var major, minor C.OM_uint32
	var mechs C.gss_OID_set

	major = C.gss_add_cred(&minor, handle, name, &mech, usage, itime, atime, &handle, &mechs, &itime, &atime)
	C.free_oid(mech)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	outputCredHandleRec = CredHandle(handle)
	actualMechs = coidSetToOids(mechs)
	C.free_oid_set(mechs)
	initiatorTimeRec = uint32(itime)
	acceptorTimeRec = uint32(atime)
	return
}

func InquireCredByMech(credHandle CredHandle, mechType asn1.ObjectIdentifier) (majorStatus, minorStatus uint32, credName InternalName, initiatorLifetimeRec, acceptorLifetimeRec, credUsage uint32) {
	handle := C.gss_cred_id_t(credHandle)
	mech := oidToCOid(mechType)
	var major, minor, ilife, alife C.OM_uint32
	var name C.gss_name_t
	var usage C.gss_cred_usage_t

	major = C.gss_inquire_cred_by_mech(&minor, handle, &mech, &name, &ilife, &alife, &usage)
	C.free_oid(mech)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	credName = InternalName(name)
	initiatorLifetimeRec = uint32(ilife)
	acceptorLifetimeRec = uint32(alife)
	credUsage = uint32(usage)
	return
}

func InitSecContext(claimantCredHandle CredHandle, contextHandle *ContextHandle, targName InternalName, mechType asn1.ObjectIdentifier, reqFlags Flags, lifetimeReq uint32, chanBindings *ChannelBindings, inputToken []byte) (majorStatus, minorStatus uint32, mechTypeRec asn1.ObjectIdentifier, outputToken []byte, recFlags Flags, transState, protReadyState bool, lifetimeRec uint32) {
	handle := C.gss_cred_id_t(claimantCredHandle)
	ctx := C.gss_ctx_id_t(*contextHandle)
	name := C.gss_name_t(targName)
	desired := oidToCOid(mechType)
	lifetime := C.OM_uint32(lifetimeReq)
	bindings := bindingsToCBindings(chanBindings)
	var major, minor, flags C.OM_uint32
	var itoken, otoken C.gss_buffer_desc
	var actual C.gss_OID

	if inputToken != nil {
		itoken = bytesToBuffer(inputToken)
	}
	if reqFlags.deleg {
		flags |= C.GSS_C_DELEG_FLAG
	}
	if reqFlags.delegPolicy {
		flags |= C.GSS_C_DELEG_POLICY_FLAG
	}
	if reqFlags.mutual {
		flags |= C.GSS_C_MUTUAL_FLAG
	}
	if reqFlags.replay {
		flags |= C.GSS_C_REPLAY_FLAG
	}
	if reqFlags.sequence {
		flags |= C.GSS_C_SEQUENCE_FLAG
	}
	if reqFlags.anon {
		flags |= C.GSS_C_ANON_FLAG
	}
	if reqFlags.conf {
		flags |= C.GSS_C_CONF_FLAG
	}
	if reqFlags.integ {
		flags |= C.GSS_C_INTEG_FLAG
	}

	major = C.gss_init_sec_context(&minor, handle, &ctx, name, &desired, flags, lifetime, bindings, &itoken, &actual, &otoken, &flags, &lifetime)
	C.free_oid(desired)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	*contextHandle = ContextHandle(ctx)
	if actual != nil {
		mechTypeRec = coidToOid(*actual)
		major = C.gss_release_oid(&minor, &actual)
	}
	if otoken.length > 0 {
		outputToken = bufferToBytes(otoken)
		major = C.gss_release_buffer(&minor, &otoken)
	}
	if flags & C.GSS_C_DELEG_FLAG != 0 {
		recFlags.deleg = true
	}
	if flags & C.GSS_C_DELEG_POLICY_FLAG != 0 {
		recFlags.delegPolicy = true
	}
	if flags & C.GSS_C_MUTUAL_FLAG != 0 {
		recFlags.mutual = true
	}
	if flags & C.GSS_C_REPLAY_FLAG != 0 {
		recFlags.replay = true
	}
	if flags & C.GSS_C_SEQUENCE_FLAG != 0 {
		recFlags.sequence = true
	}
	if flags & C.GSS_C_ANON_FLAG != 0 {
		recFlags.anon = true
	}
	if flags & C.GSS_C_CONF_FLAG != 0 {
		recFlags.conf = true
	}
	if flags & C.GSS_C_INTEG_FLAG != 0 {
		recFlags.integ = true
	}
	if flags & C.GSS_C_TRANS_FLAG != 0 {
		transState = true
	}
	if flags & C.GSS_C_PROT_READY_FLAG != 0 {
		protReadyState = true
	}
	lifetimeRec = uint32(lifetime)
	return
}

func AcceptSecContext(acceptorCredHandle CredHandle, contextHandle *ContextHandle, chanBindings *ChannelBindings, inputToken []byte) (majorStatus, minorStatus uint32, srcName InternalName, mechType asn1.ObjectIdentifier, recFlags Flags, transState, protReadyState bool, lifetimeRec uint32, delegatedCredHandle CredHandle, outputToken []byte) {
	handle := C.gss_cred_id_t(acceptorCredHandle)
	ctx := C.gss_ctx_id_t(*contextHandle)
	bindings := bindingsToCBindings(chanBindings)
	var major, minor, flags, lifetime C.OM_uint32
	var name C.gss_name_t
	var itoken, otoken C.gss_buffer_desc
	var actual C.gss_OID
	var dhandle C.gss_cred_id_t

	if inputToken != nil {
		itoken = bytesToBuffer(inputToken)
	}

	major = C.gss_accept_sec_context(&minor, &ctx, handle, &itoken, bindings, &name, &actual, &otoken, &flags, &lifetime, &dhandle)
	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	srcName = InternalName(name)
	if actual != nil {
		mechType = coidToOid(*actual)
		major = C.gss_release_oid(&minor, &actual)
	}
	*contextHandle = ContextHandle(ctx)
	if flags & C.GSS_C_DELEG_FLAG != 0 {
		recFlags.deleg = true
	}
	if flags & C.GSS_C_MUTUAL_FLAG != 0 {
		recFlags.mutual = true
	}
	if flags & C.GSS_C_REPLAY_FLAG != 0 {
		recFlags.replay = true
	}
	if flags & C.GSS_C_SEQUENCE_FLAG != 0 {
		recFlags.sequence = true
	}
	if flags & C.GSS_C_ANON_FLAG != 0 {
		recFlags.anon = true
	}
	if flags & C.GSS_C_CONF_FLAG != 0 {
		recFlags.conf = true
	}
	if flags & C.GSS_C_INTEG_FLAG != 0 {
		recFlags.integ = true
	}
	if flags & C.GSS_C_TRANS_FLAG != 0 {
		transState = true
	}
	if flags & C.GSS_C_PROT_READY_FLAG != 0 {
		protReadyState = true
	}
	lifetimeRec = uint32(lifetime)
	delegatedCredHandle = CredHandle(dhandle)
	if otoken.length > 0 {
		outputToken = bufferToBytes(otoken)
		major = C.gss_release_buffer(&minor, &otoken)
	}
	return
}

func DeleteSecContext(contextHandle ContextHandle) (majorStatus, minorStatus uint32, outputContextToken []byte) {
	handle := C.gss_ctx_id_t(contextHandle)
	var major, minor C.OM_uint32<commit_msg>Let go fmt do its thing<commit_after>package gss

/*
#cgo pkg-config: krb5-gssapi
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>
#include <gssapi/gssapi_krb5.h>
#include <gssapi/gssapi_ext.h>

static gss_OID_desc nth_oid_in_set(gss_OID_set_desc *oset, unsigned int n)
{
	return oset->elements[n];
}
static gss_buffer_desc nth_buffer_in_set(gss_buffer_set_desc *bset, unsigned int n)
{
	return bset->elements[n];
}
static gss_key_value_element_desc *alloc_n_kvset_elems(unsigned int n)
{
	return malloc(sizeof(gss_key_value_element_desc));
}
static void kv_set(gss_key_value_set_desc *kvset, int i, char *key, char *value)
{
	kvset->elements[i].key = key;
	kvset->elements[i].value = value;
}
static void free_kv_set(gss_key_value_set_desc kvset)
{
	unsigned i;
	for (i = 0; i < kvset.count; i++) {
		free((char *) kvset.elements[i].key);
		free((char *) kvset.elements[i].value);
	}
	free(kvset.elements);
}
static void free_gss_buffer(gss_buffer_desc buffer)
{
	free(buffer.value);
}
static void free_oid(gss_OID_desc oid)
{
	free(oid.elements);
}
static void free_oid_set(gss_OID_set buffer)
{
	OM_uint32 minor;
	gss_release_oid_set(&minor, &buffer);
}
static void *
copyOid(unsigned char *bytes, int len)
{
	void *ret;

	if (len < 0) {
		return NULL;
	}
	ret = malloc(len);
	if (ret != NULL) {
		memcpy(ret, bytes, len);
	}
	return ret;
}
*/
import "C"
import "unsafe"
import "encoding/asn1"
import "fmt"

const (
	C_DCE_STYLE           = C.GSS_C_DCE_STYLE
	C_IDENTIFY_FLAG       = C.GSS_C_IDENTIFY_FLAG
	C_EXTENDED_ERROR_FLAG = C.GSS_C_EXTENDED_ERROR_FLAG

	//	C_NO_IOV_BUFFER = C.GSS_C_NO_IOV_BUFFER

	IOV_BUFFER_TYPE_EMPTY       = C.GSS_IOV_BUFFER_TYPE_EMPTY
	IOV_BUFFER_TYPE_DATA        = C.GSS_IOV_BUFFER_TYPE_DATA
	IOV_BUFFER_TYPE_HEADER      = C.GSS_IOV_BUFFER_TYPE_HEADER
	IOV_BUFFER_TYPE_MECH_PARAMS = C.GSS_IOV_BUFFER_TYPE_MECH_PARAMS
	IOV_BUFFER_TYPE_TRAILER     = C.GSS_IOV_BUFFER_TYPE_TRAILER
	IOV_BUFFER_TYPE_PADDING     = C.GSS_IOV_BUFFER_TYPE_PADDING
	IOV_BUFFER_TYPE_STREAM      = C.GSS_IOV_BUFFER_TYPE_STREAM
	IOV_BUFFER_TYPE_SIGN_ONLY   = C.GSS_IOV_BUFFER_TYPE_SIGN_ONLY
	IOV_BUFFER_TYPE_MIC_TOKEN   = C.GSS_IOV_BUFFER_TYPE_MIC_TOKEN
	IOV_BUFFER_FLAG_MASK        = C.GSS_IOV_BUFFER_FLAG_MASK
	IOV_BUFFER_FLAG_ALLOCATE    = C.GSS_IOV_BUFFER_FLAG_ALLOCATE
	IOV_BUFFER_FLAG_ALLOCATED   = C.GSS_IOV_BUFFER_FLAG_ALLOCATED

	//	C_NO_CRED_STORE

	C_BOTH     = C.GSS_C_BOTH
	C_INITIATE = C.GSS_C_INITIATE
	C_ACCEPT   = C.GSS_C_ACCEPT

	C_GSS_CODE  = C.GSS_C_GSS_CODE
	C_MECH_CODE = C.GSS_C_MECH_CODE

	C_AF_UNSPEC    = C.GSS_C_AF_UNSPEC
	C_AF_LOCAL     = C.GSS_C_AF_LOCAL
	C_AF_INET      = C.GSS_C_AF_INET
	C_AF_IMPLINK   = C.GSS_C_AF_IMPLINK
	C_AF_PUP       = C.GSS_C_AF_PUP
	C_AF_CHAOS     = C.GSS_C_AF_CHAOS
	C_AF_NS        = C.GSS_C_AF_NS
	C_AF_NBS       = C.GSS_C_AF_NBS
	C_AF_ECMA      = C.GSS_C_AF_ECMA
	C_AF_DATAKIT   = C.GSS_C_AF_DATAKIT
	C_AF_CCITT     = C.GSS_C_AF_CCITT
	C_AF_SNA       = C.GSS_C_AF_SNA
	C_AF_DECnet    = C.GSS_C_AF_DECnet
	C_AF_DLI       = C.GSS_C_AF_DLI
	C_AF_LAT       = C.GSS_C_AF_LAT
	C_AF_HYLINK    = C.GSS_C_AF_HYLINK
	C_AF_APPLETALK = C.GSS_C_AF_APPLETALK
	C_AF_BSC       = C.GSS_C_AF_BSC
	C_AF_DSS       = C.GSS_C_AF_DSS
	C_AF_OSI       = C.GSS_C_AF_OSI
	C_AF_NETBIOS   = C.GSS_C_AF_NETBIOS
	C_AF_X25       = C.GSS_C_AF_X25
	C_AF_NULLADDR  = C.GSS_C_AF_NULLADDR

	//	C_NO_NAME = nil
	//	C_NO_BUFFER = nil
	//	C_NO_OID = nil
	//	C_NO_OID_SET = nil
	//	C_NO_CONTEXT = nil
	//	C_NO_CREDENTIAL = nil
	//	C_NO_CHANNEL_BINDINGS = nil
	//	C_EMPTY_BUFFER = make([]byte, 0)

	//	C_NULL_OID = nil
	//	C_NULL_OID_SET = nil

	C_QOP_DEFAULT = C.GSS_C_QOP_DEFAULT
	C_INDEFINITE  = C.GSS_C_INDEFINITE

	S_COMPLETE                = C.GSS_S_COMPLETE
	C_CALLING_ERROR_OFFSET    = C.GSS_C_CALLING_ERROR_OFFSET
	C_ROUTINE_ERROR_OFFSET    = C.GSS_C_ROUTINE_ERROR_OFFSET
	C_SUPPLEMENTARY_OFFSET    = C.GSS_C_SUPPLEMENTARY_OFFSET
	C_CALLING_ERROR_MASK      = C.GSS_C_CALLING_ERROR_MASK
	C_ROUTINE_ERROR_MASK      = C.GSS_C_ROUTINE_ERROR_MASK
	C_SUPPLEMENTARY_MASK      = C.GSS_C_SUPPLEMENTARY_MASK
	S_CALL_INACCESSIBLE_READ  = C.GSS_S_CALL_INACCESSIBLE_READ
	S_CALL_INACCESSIBLE_WRITE = C.GSS_S_CALL_INACCESSIBLE_WRITE
	S_CALL_BAD_STRUCTURE      = C.GSS_S_CALL_BAD_STRUCTURE
	S_BAD_MECH                = C.GSS_S_BAD_MECH
	S_BAD_NAME                = C.GSS_S_BAD_NAME
	S_BAD_NAMETYPE            = C.GSS_S_BAD_NAMETYPE
	S_BAD_BINDINGS            = C.GSS_S_BAD_BINDINGS
	S_BAD_STATUS              = C.GSS_S_BAD_STATUS
	S_BAD_SIG                 = C.GSS_S_BAD_SIG
	S_NO_CRED                 = C.GSS_S_NO_CRED
	S_NO_CONTEXT              = C.GSS_S_NO_CONTEXT
	S_DEFECTIVE_TOKEN         = C.GSS_S_DEFECTIVE_TOKEN
	S_DEFECTIVE_CREDENTIAL    = C.GSS_S_DEFECTIVE_CREDENTIAL
	S_CREDENTIALS_EXPIRED     = C.GSS_S_CREDENTIALS_EXPIRED
	S_CONTEXT_EXPIRED         = C.GSS_S_CONTEXT_EXPIRED
	S_FAILURE                 = C.GSS_S_FAILURE
	S_BAD_QOP                 = C.GSS_S_BAD_QOP
	S_UNAUTHORIZED            = C.GSS_S_UNAUTHORIZED
	S_UNAVAILABLE             = C.GSS_S_UNAVAILABLE
	S_DUPLICATE_ELEMENT       = C.GSS_S_DUPLICATE_ELEMENT
	S_NAME_NOT_MN             = C.GSS_S_NAME_NOT_MN
	S_BAD_MECH_ATTR           = C.GSS_S_BAD_MECH_ATTR
	S_CONTINUE_NEEDED         = C.GSS_S_CONTINUE_NEEDED
	S_DUPLICATE_TOKEN         = C.GSS_S_DUPLICATE_TOKEN
	S_OLD_TOKEN               = C.GSS_S_OLD_TOKEN
	S_UNSEQ_TOKEN             = C.GSS_S_UNSEQ_TOKEN
	S_GAP_TOKEN               = C.GSS_S_GAP_TOKEN
	C_PRF_KEY_FULL            = C.GSS_C_PRF_KEY_FULL
	C_PRF_KEY_PARTIAL         = C.GSS_C_PRF_KEY_PARTIAL
	S_CRED_UNAVAIL            = C.GSS_S_CRED_UNAVAIL
)

var (
	KRB5_NT_HOSTBASED_SERVICE_NAME = coidToOid(*C.GSS_KRB5_NT_HOSTBASED_SERVICE_NAME)
	KRB5_NT_USER_NAME              = coidToOid(*C.GSS_KRB5_NT_USER_NAME)
	KRB5_NT_MACHINE_UID_NAME       = coidToOid(*C.GSS_KRB5_NT_MACHINE_UID_NAME)
	KRB5_NT_STRING_UID_NAME        = coidToOid(*C.GSS_KRB5_NT_STRING_UID_NAME)

	C_INQ_SSPI_SESSION_KEY  = coidToOid(*C.GSS_C_INQ_SSPI_SESSION_KEY)
	C_ATTR_LOCAL_LOGIN_USER = bufferToBytes(*C.GSS_C_ATTR_LOCAL_LOGIN_USER)
	C_NT_COMPOSITE_EXPORT   = coidToOid(*C.GSS_C_NT_COMPOSITE_EXPORT)

	C_NT_USER_NAME           = coidToOid(*C.GSS_C_NT_USER_NAME)
	C_NT_MACHINE_UID_NAME    = coidToOid(*C.GSS_C_NT_MACHINE_UID_NAME)
	C_NT_STRING_UID_NAME     = coidToOid(*C.GSS_C_NT_STRING_UID_NAME)
	C_NT_HOSTBASED_SERVICE_X = coidToOid(*C.GSS_C_NT_HOSTBASED_SERVICE_X)
	C_NT_HOSTBASED_SERVICE   = coidToOid(*C.GSS_C_NT_HOSTBASED_SERVICE)
	C_NT_ANONYMOUS           = coidToOid(*C.GSS_C_NT_ANONYMOUS)
	C_NT_EXPORT_NAME         = coidToOid(*C.GSS_C_NT_EXPORT_NAME)

	C_MA_MECH_CONCRETE  = coidToOid(*C.GSS_C_MA_MECH_CONCRETE)
	C_MA_MECH_PSEUDO    = coidToOid(*C.GSS_C_MA_MECH_PSEUDO)
	C_MA_MECH_COMPOSITE = coidToOid(*C.GSS_C_MA_MECH_COMPOSITE)
	C_MA_MECH_NEGO      = coidToOid(*C.GSS_C_MA_MECH_NEGO)
	C_MA_MECH_GLUE      = coidToOid(*C.GSS_C_MA_MECH_GLUE)
	C_MA_NOT_MECH       = coidToOid(*C.GSS_C_MA_NOT_MECH)
	C_MA_DEPRECATED     = coidToOid(*C.GSS_C_MA_DEPRECATED)
	C_MA_NOT_DFLT_MECH  = coidToOid(*C.GSS_C_MA_NOT_DFLT_MECH)
	C_MA_ITOK_FRAMED    = coidToOid(*C.GSS_C_MA_ITOK_FRAMED)
	C_MA_AUTH_INIT      = coidToOid(*C.GSS_C_MA_AUTH_INIT)
	C_MA_AUTH_TARG      = coidToOid(*C.GSS_C_MA_AUTH_TARG)
	C_MA_AUTH_INIT_INIT = coidToOid(*C.GSS_C_MA_AUTH_INIT_INIT)
	C_MA_AUTH_TARG_INIT = coidToOid(*C.GSS_C_MA_AUTH_TARG_INIT)
	C_MA_AUTH_INIT_ANON = coidToOid(*C.GSS_C_MA_AUTH_INIT_ANON)
	C_MA_AUTH_TARG_ANON = coidToOid(*C.GSS_C_MA_AUTH_TARG_ANON)
	C_MA_DELEG_CRED     = coidToOid(*C.GSS_C_MA_DELEG_CRED)
	C_MA_INTEG_PROT     = coidToOid(*C.GSS_C_MA_INTEG_PROT)
	C_MA_CONF_PROT      = coidToOid(*C.GSS_C_MA_CONF_PROT)
	C_MA_MIC            = coidToOid(*C.GSS_C_MA_MIC)
	C_MA_WRAP           = coidToOid(*C.GSS_C_MA_WRAP)
	C_MA_PROT_READY     = coidToOid(*C.GSS_C_MA_PROT_READY)
	C_MA_REPLAY_DET     = coidToOid(*C.GSS_C_MA_REPLAY_DET)
	C_MA_OOS_DET        = coidToOid(*C.GSS_C_MA_OOS_DET)
	C_MA_CBINDINGS      = coidToOid(*C.GSS_C_MA_CBINDINGS)
	C_MA_PFS            = coidToOid(*C.GSS_C_MA_PFS)
	C_MA_COMPRESS       = coidToOid(*C.GSS_C_MA_COMPRESS)
	C_MA_CTX_TRANS      = coidToOid(*C.GSS_C_MA_CTX_TRANS)

	KRB5_NT_PRINCIPAL_NAME = coidToOid(*C.GSS_KRB5_NT_PRINCIPAL_NAME)

	Mech_krb5          = coidToOid(*C.gss_mech_krb5)
	Mech_krb5_old      = coidToOid(*C.gss_mech_krb5_old)
	Mech_krb5_wrong    = coidToOid(*C.gss_mech_krb5_wrong)
	Mech_iakerb        = coidToOid(*C.gss_mech_iakerb)
	Mech_set_krb5      = coidSetToOids(C.gss_mech_set_krb5)
	Mech_set_krb5_old  = coidSetToOids(C.gss_mech_set_krb5_old)
	Mech_set_krb5_both = coidSetToOids(C.gss_mech_set_krb5_both)

	NT_krb5_name      = coidToOid(*C.gss_nt_krb5_name)
	NT_krb5_principal = coidToOid(*C.gss_nt_krb5_principal)
	//Krb5_gss_oid_array = coidToOid(C.krb5_gss_oid_array)
)

type CredHandle C.gss_cred_id_t

type ContextHandle C.gss_ctx_id_t

type InternalName C.gss_name_t

type ChannelBindings struct {
	initiatorAddressType, acceptorAddressType          uint32
	initiatorAddress, acceptorAddress, applicationData []byte
}

type Flags struct {
	deleg, delegPolicy, mutual, replay, sequence, anon, conf, integ bool
}

type IOV struct {
	data []byte
}

/* bytesToBuffer populates a gss_buffer_t with a borrowed reference to the
 * contents of the slice. */
func bytesToBuffer(data []byte) (cdesc C.gss_buffer_desc) {
	value := unsafe.Pointer(&data[0])
	length := C.size_t(len(data))

	cdesc.value = value
	cdesc.length = length
	return
}

/* bufferToBytes creates a byte array using the contents of the passed-in
 * buffer. */
func bufferToBytes(cdesc C.gss_buffer_desc) (b []byte) {
	length := C.int(cdesc.length)

	b = C.GoBytes(cdesc.value, length)
	return
}

/* buffersToBytes creates a byte array using the contents of the passed-in
 * buffer. */
func buffersToBytes(cdesc C.gss_buffer_set_desc) (b [][]byte) {
	count := uint(cdesc.count)
	var i uint

	b = make([][]byte, count)
	for i = 0; i < count; i++ {
		b[i] = bufferToBytes(C.nth_buffer_in_set(&cdesc, C.uint(i)))
	}
	return
}

/* Encode a tag and a length as a DER definite length */
func makeTagAndLength(tag, length int) (l []byte) {
	var count, bits int

	if length <= 127 {
		l = make([]byte, 2)
		l[0] = byte(tag)
		l[1] = byte(length)
		return
	}
	count = 0
	bits = length
	for bits != 0 {
		count++
		bits = bits >> 8
	}
	if count > 126 {
		return nil
	}
	l = make([]byte, 2+count)
	count = 0
	bits = length
	l[0] = byte(tag)
	for bits != 0 {
		l[len(l)-1-count] = byte(bits & 0xff)
		count++
		bits = bits >> 8
	}
	l[1] = byte((count | 0x80) & 0x7f)
	return
}

/* Split up a DER item */
func splitTagAndLength(tlv []byte) (class int, constructed bool, tag, length int, value []byte) {
	tbytes := 1
	lbytes := 1

	class = int((tlv[0] & 0xc0) >> 6)
	constructed = (tlv[0] & 0x20) != 0
	tag = int(tlv[0] & 0x1f)
	if tag == 0x1f {
		tag = 0
		for tlv[tbytes]&0x80 != 0 {
			tag = (tag << 7) + int(tlv[tbytes]&0x7f)
			tbytes++
		}
		tag = (tag << 7) + int(tlv[tbytes]&0x7f)
		tbytes++
	}
	if tlv[tbytes]&0x80 == 0 {
		length = int(tlv[tbytes] & 0x7f)
	} else {
		lbytes = int(tlv[tbytes] & 0x7f)
		if lbytes == 0 {
			value = nil
			return
		}
		for count := 0; count < lbytes; count++ {
			length = (length << 8) + int(tlv[tbytes+count]&0xff)
		}
	}
	if len(tlv) != tbytes+lbytes+length {
		value = nil
		return
	}
	value = tlv[(tbytes + lbytes):]
	return
}

/* coidToOid produces an asn1.ObjectIdentifier from the library's preferred
 * bytes-and-length representation, which is just the DER encoding without a
 * tag and length. */
func coidToOid(coid C.gss_OID_desc) (oid asn1.ObjectIdentifier) {
	length := C.int(coid.length)
	b := C.GoBytes(coid.elements, length)

	b = append(makeTagAndLength(6, len(b)), b...)
	asn1.Unmarshal(b, &oid)
	return
}

/* oidToCOid converts an asn1.ObjectIdentifier into an array of encoded bytes
 * without the tag and length, which is how the C library expects them to be
 * structured. */
func oidToCOid(oid asn1.ObjectIdentifier) (coid C.gss_OID_desc) {
	b, _ := asn1.Marshal(oid)
	if b == nil {
		return
	}
	_, _, _, _, v := splitTagAndLength(b)
	if v == nil {
		return
	}
	length := len(v)
	coid.elements = C.copyOid((*C.uchar)(&v[0]), C.int(length))
	coid.length = C.OM_uint32(length)
	return
}

/* oidsToCOidSet converts an array of asn1.ObjectIdentifier items into an array
 * of arrays of encoded bytes-and-lengths, which is how the C library expects
 * them to be structured. */
func oidsToCOidSet(oidSet []asn1.ObjectIdentifier) (coids C.gss_OID_set) {
	var major, minor C.OM_uint32
	if oidSet == nil {
		return
	}
	major = C.gss_create_empty_oid_set(&minor, &coids)
	if major != 0 {
		return
	}

	for _, o := range oidSet {
		oid := oidToCOid(o)
		major = C.gss_add_oid_set_member(&minor, &oid, &coids)
		C.free_oid(oid)
		if major != 0 {
			major = C.gss_release_oid_set(&minor, &coids)
			coids = nil
			return
		}
	}
	return
}

/* coidSetToOids produces an array of asn1.ObjectIdentifier items from the
 * library's preferred array-of-bytes-and-lengths representation. */
func coidSetToOids(coids *C.gss_OID_set_desc) (oidSet []asn1.ObjectIdentifier) {
	if coids == nil {
		return nil
	}

	oidSet = make([]asn1.ObjectIdentifier, coids.count)
	if oidSet == nil {
		return
	}

	for o := 0; o < int(coids.count); o++ {
		coid := C.nth_oid_in_set(coids, C.uint(o))
		oidSet[o] = coidToOid(coid)
	}

	return
}

func bindingsToCBindings(bindings *ChannelBindings) (cbindings C.gss_channel_bindings_t) {
	if bindings == nil {
		return nil
	}
	cbindings.initiator_addrtype = C.OM_uint32(bindings.initiatorAddressType)
	cbindings.initiator_address = bytesToBuffer(bindings.initiatorAddress)
	cbindings.acceptor_addrtype = C.OM_uint32(bindings.acceptorAddressType)
	cbindings.acceptor_address = bytesToBuffer(bindings.acceptorAddress)
	cbindings.application_data = bytesToBuffer(bindings.applicationData)
	return
}

func cbindingsToBindings(cbindings C.gss_channel_bindings_t) (bindings *ChannelBindings) {
	if cbindings == nil {
		return nil
	}
	bindings.initiatorAddressType = uint32(cbindings.initiator_addrtype)
	bindings.initiatorAddress = bufferToBytes(cbindings.initiator_address)
	bindings.acceptorAddressType = uint32(cbindings.acceptor_addrtype)
	bindings.acceptorAddress = bufferToBytes(cbindings.acceptor_address)
	bindings.applicationData = bufferToBytes(cbindings.application_data)
	return
}

func credStoreToKVSet(credStore [][2]string) (kvset C.gss_key_value_set_desc) {
	kvset.elements = C.alloc_n_kvset_elems(C.uint(len(credStore)))
	if kvset.elements == nil {
		return
	}
	for i, kv := range credStore {
		C.kv_set(&kvset, C.int(i), C.CString(kv[0]), C.CString(kv[1]))
	}
	kvset.count = C.OM_uint32(len(credStore))
	return
}

func AcquireCred(desiredName InternalName, lifetimeReq uint32, desiredMechs []asn1.ObjectIdentifier, credUsage uint32) (majorStatus, minorStatus uint32, outputCredHandle CredHandle, actualMechs []asn1.ObjectIdentifier, lifetimeRec uint32) {
	name := C.gss_name_t(desiredName)
	lifetime := C.OM_uint32(lifetimeReq)
	usage := C.gss_cred_usage_t(credUsage)
	var major, minor C.OM_uint32
	var desired, actual C.gss_OID_set
	var handle C.gss_cred_id_t

	desired = oidsToCOidSet(desiredMechs)
	major = C.gss_acquire_cred(&minor, name, lifetime, desired, usage, &handle, &actual, &lifetime)
	C.free_oid_set(desired)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	outputCredHandle = CredHandle(handle)
	actualMechs = coidSetToOids(actual)
	C.free_oid_set(actual)
	lifetimeRec = uint32(lifetime)
	return
}

func ReleaseCred(credHandle CredHandle) (majorStatus, minorStatus uint32) {
	handle := C.gss_cred_id_t(credHandle)
	var major, minor C.OM_uint32

	major = C.gss_release_cred(&minor, &handle)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	return
}

func InquireCred(credHandle CredHandle) (majorStatus, minorStatus uint32, credName InternalName, lifetimeRec, credUsage uint32, mechSet []asn1.ObjectIdentifier) {
	handle := C.gss_cred_id_t(credHandle)
	name := C.gss_name_t(nil)
	var major, minor, lifetime C.OM_uint32
	var usage C.gss_cred_usage_t
	var mechs C.gss_OID_set

	major = C.gss_inquire_cred(&minor, handle, &name, &lifetime, &usage, &mechs)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	credName = InternalName(name)
	lifetimeRec = uint32(lifetime)
	credUsage = uint32(usage)
	mechSet = coidSetToOids(mechs)
	C.free_oid_set(mechs)
	return
}

func AddCred(credHandle CredHandle, desiredName InternalName, desiredMech asn1.ObjectIdentifier, initiatorTimeReq, acceptorTimeReq, credUsage uint32, outputCredHandle CredHandle) (majorStatus, minorStatus uint32, outputCredHandleRec CredHandle, actualMechs []asn1.ObjectIdentifier, initiatorTimeRec, acceptorTimeRec uint32) {
	handle := C.gss_cred_id_t(credHandle)
	name := C.gss_name_t(desiredName)
	mech := oidToCOid(desiredMech)
	itime := C.OM_uint32(initiatorTimeReq)
	atime := C.OM_uint32(acceptorTimeReq)
	usage := C.gss_cred_usage_t(credUsage)
	var major, minor C.OM_uint32
	var mechs C.gss_OID_set

	major = C.gss_add_cred(&minor, handle, name, &mech, usage, itime, atime, &handle, &mechs, &itime, &atime)
	C.free_oid(mech)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	outputCredHandleRec = CredHandle(handle)
	actualMechs = coidSetToOids(mechs)
	C.free_oid_set(mechs)
	initiatorTimeRec = uint32(itime)
	acceptorTimeRec = uint32(atime)
	return
}

func InquireCredByMech(credHandle CredHandle, mechType asn1.ObjectIdentifier) (majorStatus, minorStatus uint32, credName InternalName, initiatorLifetimeRec, acceptorLifetimeRec, credUsage uint32) {
	handle := C.gss_cred_id_t(credHandle)
	mech := oidToCOid(mechType)
	var major, minor, ilife, alife C.OM_uint32
	var name C.gss_name_t
	var usage C.gss_cred_usage_t

	major = C.gss_inquire_cred_by_mech(&minor, handle, &mech, &name, &ilife, &alife, &usage)
	C.free_oid(mech)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	credName = InternalName(name)
	initiatorLifetimeRec = uint32(ilife)
	acceptorLifetimeRec = uint32(alife)
	credUsage = uint32(usage)
	return
}

func InitSecContext(claimantCredHandle CredHandle, contextHandle *ContextHandle, targName InternalName, mechType asn1.ObjectIdentifier, reqFlags Flags, lifetimeReq uint32, chanBindings *ChannelBindings, inputToken []byte) (majorStatus, minorStatus uint32, mechTypeRec asn1.ObjectIdentifier, outputToken []byte, recFlags Flags, transState, protReadyState bool, lifetimeRec uint32) {
	handle := C.gss_cred_id_t(claimantCredHandle)
	ctx := C.gss_ctx_id_t(*contextHandle)
	name := C.gss_name_t(targName)
	desired := oidToCOid(mechType)
	lifetime := C.OM_uint32(lifetimeReq)
	bindings := bindingsToCBindings(chanBindings)
	var major, minor, flags C.OM_uint32
	var itoken, otoken C.gss_buffer_desc
	var actual C.gss_OID

	if inputToken != nil {
		itoken = bytesToBuffer(inputToken)
	}
	if reqFlags.deleg {
		flags |= C.GSS_C_DELEG_FLAG
	}
	if reqFlags.delegPolicy {
		flags |= C.GSS_C_DELEG_POLICY_FLAG
	}
	if reqFlags.mutual {
		flags |= C.GSS_C_MUTUAL_FLAG
	}
	if reqFlags.replay {
		flags |= C.GSS_C_REPLAY_FLAG
	}
	if reqFlags.sequence {
		flags |= C.GSS_C_SEQUENCE_FLAG
	}
	if reqFlags.anon {
		flags |= C.GSS_C_ANON_FLAG
	}
	if reqFlags.conf {
		flags |= C.GSS_C_CONF_FLAG
	}
	if reqFlags.integ {
		flags |= C.GSS_C_INTEG_FLAG
	}

	major = C.gss_init_sec_context(&minor, handle, &ctx, name, &desired, flags, lifetime, bindings, &itoken, &actual, &otoken, &flags, &lifetime)
	C.free_oid(desired)

	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	*contextHandle = ContextHandle(ctx)
	if actual != nil {
		mechTypeRec = coidToOid(*actual)
		major = C.gss_release_oid(&minor, &actual)
	}
	if otoken.length > 0 {
		outputToken = bufferToBytes(otoken)
		major = C.gss_release_buffer(&minor, &otoken)
	}
	if flags&C.GSS_C_DELEG_FLAG != 0 {
		recFlags.deleg = true
	}
	if flags&C.GSS_C_DELEG_POLICY_FLAG != 0 {
		recFlags.delegPolicy = true
	}
	if flags&C.GSS_C_MUTUAL_FLAG != 0 {
		recFlags.mutual = true
	}
	if flags&C.GSS_C_REPLAY_FLAG != 0 {
		recFlags.replay = true
	}
	if flags&C.GSS_C_SEQUENCE_FLAG != 0 {
		recFlags.sequence = true
	}
	if flags&C.GSS_C_ANON_FLAG != 0 {
		recFlags.anon = true
	}
	if flags&C.GSS_C_CONF_FLAG != 0 {
		recFlags.conf = true
	}
	if flags&C.GSS_C_INTEG_FLAG != 0 {
		recFlags.integ = true
	}
	if flags&C.GSS_C_TRANS_FLAG != 0 {
		transState = true
	}
	if flags&C.GSS_C_PROT_READY_FLAG != 0 {
		protReadyState = true
	}
	lifetimeRec = uint32(lifetime)
	return
}

func AcceptSecContext(acceptorCredHandle CredHandle, contextHandle *ContextHandle, chanBindings *ChannelBindings, inputToken []byte) (majorStatus, minorStatus uint32, srcName InternalName, mechType asn1.ObjectIdentifier, recFlags Flags, transState, protReadyState bool, lifetimeRec uint32, delegatedCredHandle CredHandle, outputToken []byte) {
	handle := C.gss_cred_id_t(acceptorCredHandle)
	ctx := C.gss_ctx_id_t(*contextHandle)
	bindings := bindingsToCBindings(chanBindings)
	var major, minor, flags, lifetime C.OM_uint32
	var name C.gss_name_t
	var itoken, otoken C.gss_buffer_desc
	var actual C.gss_OID
	var dhandle C.gss_cred_id_t

	if inputToken != nil {
		itoken = bytesToBuffer(inputToken)
	}

	major = C.gss_accept_sec_context(&minor, &ctx, handle, &itoken, bindings, &name, &actual, &otoken, &flags, &lifetime, &dhandle)
	majorStatus = uint32(major)
	minorStatus = uint32(minor)
	srcName = InternalName(name)
	if actual != nil {
		mechType = coidToOid(*actual)
		major = C.gss_release_oid(&minor, &actual)
	}
	*contextHandle = ContextHandle(ctx)
	if flags&C.GSS_C_DELEG_FLAG != 0 {
		recFlags.deleg = true
	}
	if flags&C.GSS_C_MUTUAL_FLAG != 0 {
		recFlags.mutual = true
	}
	if flags&C.GSS_C_REPLAY_FLAG != 0 {
		recFlags.replay = true
	}
	if flags&C.GSS_C_SEQUENCE_FLAG != 0 {
		recFlags.sequence = true
	}
	if flags&C.GSS_C_ANON_FLAG != 0 {
		recFlags.anon = true
	}
	if flags&C.GSS_C_CONF_FLAG != 0 {
		recFlags.conf = true
	}
	if flags&C.GSS_C_INTEG_FLAG != 0 {
		recFlags.integ = true
	}
	if flags&C.GSS_C_TRANS_FLAG != 0 {
		transState = true
	}
	if flags&C.GSS_C_PROT_READY_FLAG != 0 {
		protReadyState = true
	}
	lifetimeRec = uint32(lifetime)
	delegatedCredHandle = CredHandle(dhandle)
	if otoken.length > 0 {
		outputToken = bufferToBytes(otoken)
		major = C.gss_release_buffer(&minor, &otoken)
	}
	return
}

func DeleteSecContext(contextHandle ContextHandle) (majorStatus, minorStatus uint32, outputContextToken []byte) {
	handle := C.gss_ctx_id_t(contextHandle)
	var major, minor C.OM_uint32
{'fraction_non_alphanumeric': 0.10172566271344022, 'fraction_numerical': 0.012653348148484093, 'mean_word_length': 4.923438549361988, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1572926', 'n_tokens_mistral': 20160, 'n_tokens_neox': 19430, 'n_words': 3134}