// InquireCredByMech implements gss_inquire_cred_by_mech API, as per // https://tools.ietf.org/html/rfc2743#page-39. name must be .Release()-ed by // the caller func (lib *Lib) InquireCredByMech(credHandle *CredId, mechType *OID) ( name *Name, initiatorLifetime time.Duration, acceptorLifetime time.Duration, credUsage CredUsage, err error) { min := C.OM_uint32(0) name = lib.NewName() ilife := C.OM_uint32(0) alife := C.OM_uint32(0) credUsage = CredUsage(0) maj := C.wrap_gss_inquire_cred_by_mech(lib.Fp_gss_inquire_cred_by_mech, &min, credHandle.C_gss_cred_id_t, mechType.C_gss_OID, &name.C_gss_name_t, &ilife, &alife, (*C.gss_cred_usage_t)(&credUsage)) err = lib.stashLastStatus(maj, min) if err != nil { return nil, 0, 0, 0, err } return name, time.Duration(ilife) * time.Second, time.Duration(alife) * time.Second, credUsage, nil }
// AcquireCred implements gss_acquire_cred API, as per // https://tools.ietf.org/html/rfc2743#page-31. outputCredHandle, actualMechs // must be .Release()-ed by the caller func (lib *Lib) AcquireCred(desiredName *Name, timeReq time.Duration, desiredMechs *OIDSet, credUsage CredUsage) (outputCredHandle *CredId, actualMechs *OIDSet, timeRec time.Duration, err error) { min := C.OM_uint32(0) actualMechs = lib.NewOIDSet() outputCredHandle = lib.NewCredId() timerec := C.OM_uint32(0) maj := C.wrap_gss_acquire_cred(lib.Fp_gss_acquire_cred, &min, desiredName.C_gss_name_t, C.OM_uint32(timeReq.Seconds()), desiredMechs.C_gss_OID_set, C.gss_cred_usage_t(credUsage), &outputCredHandle.C_gss_cred_id_t, &actualMechs.C_gss_OID_set, &timerec) err = lib.stashLastStatus(maj, min) if err != nil { return nil, nil, 0, err } return outputCredHandle, actualMechs, time.Duration(timerec) * time.Second, nil }
// InquireCred implements gss_inquire_cred API, as per // https://tools.ietf.org/html/rfc2743#page-34. name and mechanisms must be // .Release()-ed by the caller func (lib *Lib) InquireCred(credHandle *CredId) ( name *Name, lifetime time.Duration, credUsage CredUsage, mechanisms *OIDSet, err error) { min := C.OM_uint32(0) name = lib.NewName() life := C.OM_uint32(0) credUsage = CredUsage(0) mechanisms = lib.NewOIDSet() maj := C.wrap_gss_inquire_cred(lib.Fp_gss_inquire_cred, &min, credHandle.C_gss_cred_id_t, &name.C_gss_name_t, &life, (*C.gss_cred_usage_t)(&credUsage), &mechanisms.C_gss_OID_set) err = lib.stashLastStatus(maj, min) if err != nil { return nil, 0, 0, nil, err } return name, time.Duration(life) * time.Second, credUsage, mechanisms, nil }
// Error returns a string representation of an Error object. func (e *Error) Error() string { messages := []string{} nOther := 0 context := C.OM_uint32(0) inquiry := C.OM_uint32(0) code_type := 0 first := true if e.Major.RoutineError() == GSS_S_FAILURE { inquiry = e.Minor code_type = GSS_C_MECH_CODE } else { inquiry = C.OM_uint32(e.Major) code_type = GSS_C_GSS_CODE } for first || context != C.OM_uint32(0) { first = false min := C.OM_uint32(0) b, err := e.MakeBuffer(allocGSSAPI) if err != nil { break } // TODO: store a mech_type at the lib level? Or context? For now GSS_C_NO_OID... maj := C.wrap_gss_display_status( e.Fp_gss_display_status, &min, inquiry, C.int(code_type), nil, &context, b.C_gss_buffer_t) err = e.MakeError(maj, min).GoError() if err != nil { nOther = nOther + 1 } messages = append(messages, b.String()) b.Release() } if nOther > 0 { messages = append(messages, fmt.Sprintf("additionally, %d conversions failed", nOther)) } messages = append(messages, "") return strings.Join(messages, "\n") }
// MakeOIDBytes makes an OID encapsulating a byte slice. Note that it does not // duplicate the data, but rather it points to it directly. func (lib *Lib) MakeOIDBytes(data []byte) (*OID, error) { oid := lib.NewOID() s := C.malloc(C.gss_OID_size) // s for struct if s == nil { return nil, ErrMallocFailed } C.memset(s, 0, C.gss_OID_size) l := C.size_t(len(data)) e := C.malloc(l) // c for contents if e == nil { return nil, ErrMallocFailed } C.memmove(e, (unsafe.Pointer)(&data[0]), l) oid.C_gss_OID = C.gss_OID(s) oid.alloc = allocMalloc // because of the alignment issues I can't access o.oid's fields from go, // so invoking a C function to do the same as: // oid.C_gss_OID.length = l // oid.C_gss_OID.elements = c C.helper_gss_OID_desc_set_elements(oid.C_gss_OID, C.OM_uint32(l), e) return oid, nil }
// Unwrap implements gss_unwrap API, as per https://tools.ietf.org/html/rfc2743#page-66. // outputMessageBuffer must be .Release()-ed by the caller func (ctx *CtxId) Unwrap( inputMessageBuffer *Buffer) ( outputMessageBuffer *Buffer, confState bool, qopState QOP, err error) { min := C.OM_uint32(0) outputMessageBuffer, err = ctx.MakeBuffer(allocGSSAPI) if err != nil { return nil, false, 0, err } encrypted := C.int(0) qop := C.gss_qop_t(0) maj := C.wrap_gss_unwrap(ctx.Fp_gss_unwrap, &min, ctx.C_gss_ctx_id_t, inputMessageBuffer.C_gss_buffer_t, outputMessageBuffer.C_gss_buffer_t, &encrypted, &qop) err = ctx.stashLastStatus(maj, min) if err != nil { return nil, false, 0, err } return outputMessageBuffer, encrypted != 0, QOP(qop), nil }
// InquireContext returns fields about a security context. func (ctx *CtxId) InquireContext() ( srcName *Name, targetName *Name, lifetimeRec time.Duration, mechType *OID, ctxFlags uint64, locallyInitiated bool, open bool, err error) { min := C.OM_uint32(0) srcName = ctx.NewName() targetName = ctx.NewName() rec := C.OM_uint32(0) mechType = ctx.NewOID() flags := C.OM_uint32(0) li := C.int(0) opn := C.int(0) maj := C.wrap_gss_inquire_context(ctx.Fp_gss_inquire_context, &min, ctx.C_gss_ctx_id_t, &srcName.C_gss_name_t, &targetName.C_gss_name_t, &rec, &mechType.C_gss_OID, &flags, &li, &opn) err = ctx.stashLastStatus(maj, min) if err != nil { ctx.Err("InquireContext: ", err) return nil, nil, 0, nil, 0, false, false, err } lifetimeRec = time.Duration(rec) * time.Second ctxFlags = uint64(flags) if li != 0 { locallyInitiated = true } if opn != 0 { open = true } return srcName, targetName, lifetimeRec, mechType, ctxFlags, locallyInitiated, open, nil }
// Release frees a credential. func (c *CredId) Release() error { if c == nil || c.C_gss_cred_id_t == nil { return nil } min := C.OM_uint32(0) maj := C.wrap_gss_release_cred(c.Fp_gss_release_cred, &min, &c.C_gss_cred_id_t) return c.stashLastStatus(maj, min) }
// DeleteSecContext frees a security context. // NB: I decided not to implement the outputToken parameter since its use is no // longer recommended, and it would have to be Released by the caller func (ctx *CtxId) DeleteSecContext() error { if ctx == nil || ctx.C_gss_ctx_id_t == nil { return nil } runtime.LockOSThread() defer runtime.UnlockOSThread() min := C.OM_uint32(0) maj := C.wrap_gss_delete_sec_context(ctx.Fp_gss_delete_sec_context, &min, &ctx.C_gss_ctx_id_t, nil) return ctx.stashLastStatus(maj, min) }
// AddCred implements gss_add_cred API, as per // https://tools.ietf.org/html/rfc2743#page-36. outputCredHandle, actualMechs // must be .Release()-ed by the caller func (lib *Lib) AddCred(inputCredHandle *CredId, desiredName *Name, desiredMech *OID, credUsage CredUsage, initiatorTimeReq time.Duration, acceptorTimeReq time.Duration) ( outputCredHandle *CredId, actualMechs *OIDSet, initiatorTimeRec time.Duration, acceptorTimeRec time.Duration, err error) { min := C.OM_uint32(0) actualMechs = lib.NewOIDSet() outputCredHandle = lib.NewCredId() initSeconds := C.OM_uint32(0) acceptSeconds := C.OM_uint32(0) maj := C.wrap_gss_add_cred(lib.Fp_gss_add_cred, &min, inputCredHandle.C_gss_cred_id_t, desiredName.C_gss_name_t, desiredMech.C_gss_OID, C.gss_cred_usage_t(credUsage), C.OM_uint32(initiatorTimeReq.Seconds()), C.OM_uint32(acceptorTimeReq.Seconds()), &outputCredHandle.C_gss_cred_id_t, &actualMechs.C_gss_OID_set, &initSeconds, &acceptSeconds) err = lib.stashLastStatus(maj, min) if err != nil { return nil, nil, 0, 0, err } return outputCredHandle, actualMechs, time.Duration(initSeconds) * time.Second, time.Duration(acceptSeconds) * time.Second, nil }
// VerifyMIC implements gss_VerifyMIC API, as per https://tools.ietf.org/html/rfc2743#page-64. func (ctx *CtxId) VerifyMIC(messageBuffer *Buffer, tokenBuffer *Buffer) ( qopState QOP, err error) { min := C.OM_uint32(0) qop := C.gss_qop_t(0) maj := C.wrap_gss_verify_mic(ctx.Fp_gss_verify_mic, &min, ctx.C_gss_ctx_id_t, messageBuffer.C_gss_buffer_t, tokenBuffer.C_gss_buffer_t, &qop) err = ctx.stashLastStatus(maj, min) if err != nil { return 0, err } return QOP(qop), nil }
// Wrap implements gss_wrap API, as per https://tools.ietf.org/html/rfc2743#page-65. // outputMessageBuffer must be .Release()-ed by the caller func (ctx *CtxId) Wrap( confReq bool, qopReq QOP, inputMessageBuffer *Buffer) ( confState bool, outputMessageBuffer *Buffer, err error) { min := C.OM_uint32(0) encrypt := C.int(0) if confReq { encrypt = 1 } outputMessageBuffer, err = ctx.MakeBuffer(allocGSSAPI) if err != nil { return false, nil, err } encrypted := C.int(0) maj := C.wrap_gss_wrap(ctx.Fp_gss_wrap, &min, ctx.C_gss_ctx_id_t, encrypt, C.gss_qop_t(qopReq), inputMessageBuffer.C_gss_buffer_t, &encrypted, outputMessageBuffer.C_gss_buffer_t) err = ctx.stashLastStatus(maj, min) if err != nil { return false, nil, err } return encrypted != 0, outputMessageBuffer, nil }
// GetMIC implements gss_GetMIC API, as per https://tools.ietf.org/html/rfc2743#page-63. // messageToken must be .Release()-ed by the caller. func (ctx *CtxId) GetMIC(qopReq QOP, messageBuffer *Buffer) ( messageToken *Buffer, err error) { min := C.OM_uint32(0) token, err := ctx.MakeBuffer(allocGSSAPI) if err != nil { return nil, err } maj := C.wrap_gss_get_mic(ctx.Fp_gss_get_mic, &min, ctx.C_gss_ctx_id_t, C.gss_qop_t(qopReq), messageBuffer.C_gss_buffer_t, token.C_gss_buffer_t) err = ctx.stashLastStatus(maj, min) if err != nil { return nil, err } return token, nil }
// AcceptSecContext accepts an initialized security context. Usually called by // the server. May return ErrContinueNeeded if the client is to make another // iteration of exchanging token with the service func (lib *Lib) AcceptSecContext( ctxIn *CtxId, acceptorCredHandle *CredId, inputToken *Buffer, inputChanBindings ChannelBindings) ( ctxOut *CtxId, srcName *Name, actualMechType *OID, outputToken *Buffer, retFlags uint32, timeRec time.Duration, delegatedCredHandle *CredId, err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() // prepare the inputs C_acceptorCredHandle := C.gss_cred_id_t(nil) if acceptorCredHandle != nil { C_acceptorCredHandle = acceptorCredHandle.C_gss_cred_id_t } C_inputToken := C.gss_buffer_t(nil) if inputToken != nil { C_inputToken = inputToken.C_gss_buffer_t } // prepare the outputs if ctxIn != nil { ctxCopy := *ctxIn ctxOut = &ctxCopy } else { ctxOut = lib.GSS_C_NO_CONTEXT } min := C.OM_uint32(0) srcName = lib.NewName() actualMechType = lib.NewOID() outputToken, err = lib.MakeBuffer(allocGSSAPI) if err != nil { return nil, nil, nil, nil, 0, 0, nil, err } flags := C.OM_uint32(0) timerec := C.OM_uint32(0) delegatedCredHandle = lib.NewCredId() maj := C.wrap_gss_accept_sec_context(lib.Fp_gss_accept_sec_context, &min, &ctxOut.C_gss_ctx_id_t, // used as both in and out param C_acceptorCredHandle, C_inputToken, C.gss_channel_bindings_t(inputChanBindings), &srcName.C_gss_name_t, &actualMechType.C_gss_OID, outputToken.C_gss_buffer_t, &flags, &timerec, &delegatedCredHandle.C_gss_cred_id_t) err = lib.stashLastStatus(maj, min) if err != nil { lib.Err("AcceptSecContext: ", err) return nil, nil, nil, nil, 0, 0, nil, err } if MajorStatus(maj).ContinueNeeded() { err = ErrContinueNeeded } return ctxOut, srcName, actualMechType, outputToken, uint32(flags), time.Duration(timerec) * time.Second, delegatedCredHandle, err }
// InitSecContext initiates a security context. Usually invoked by the client. // A Context (CtxId) describes the state at one end of an authentication // protocol. May return ErrContinueNeeded if the client is to make another // iteration of exchanging token with the service func (lib *Lib) InitSecContext(initiatorCredHandle *CredId, ctxIn *CtxId, targetName *Name, mechType *OID, reqFlags uint32, timeReq time.Duration, inputChanBindings ChannelBindings, inputToken *Buffer) ( ctxOut *CtxId, actualMechType *OID, outputToken *Buffer, retFlags uint32, timeRec time.Duration, err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() // prepare the input params C_initiator := C.gss_cred_id_t(nil) if initiatorCredHandle != nil { C_initiator = initiatorCredHandle.C_gss_cred_id_t } C_mechType := C.gss_OID(nil) if mechType != nil { C_mechType = mechType.C_gss_OID } C_inputToken := C.gss_buffer_t(nil) if inputToken != nil { C_inputToken = inputToken.C_gss_buffer_t } // prepare the outputs. if ctxIn != nil { ctxCopy := *ctxIn ctxOut = &ctxCopy } else { ctxOut = lib.NewCtxId() } min := C.OM_uint32(0) actualMechType = lib.NewOID() outputToken, err = lib.MakeBuffer(allocGSSAPI) if err != nil { return nil, nil, nil, 0, 0, err } flags := C.OM_uint32(0) timerec := C.OM_uint32(0) maj := C.wrap_gss_init_sec_context(lib.Fp_gss_init_sec_context, &min, C_initiator, &ctxOut.C_gss_ctx_id_t, // used as both in and out param targetName.C_gss_name_t, C_mechType, C.OM_uint32(reqFlags), C.OM_uint32(timeReq.Seconds()), C.gss_channel_bindings_t(inputChanBindings), C_inputToken, &actualMechType.C_gss_OID, outputToken.C_gss_buffer_t, &flags, &timerec) err = lib.stashLastStatus(maj, min) if err != nil { return nil, nil, nil, 0, 0, err } if MajorStatus(maj).ContinueNeeded() { err = ErrContinueNeeded } return ctxOut, actualMechType, outputToken, uint32(flags), time.Duration(timerec) * time.Second, err }