// Copyright 2013-2015 Apcera Inc. All rights reserved.
// GSS status and errors
package gssapi
/*
#include <gssapi/gssapi.h>
OM_uint32
wrap_gss_display_status(void *fp,
OM_uint32 *minor_status,
OM_uint32 status_value,
int status_type,
const gss_OID mech_type,
OM_uint32 *message_context,
gss_buffer_t status_string)
{
return ((OM_uint32(*)(
OM_uint32 *,
OM_uint32,
int,
const gss_OID,
OM_uint32 *,
gss_buffer_t)
)fp)(minor_status,
status_value,
status_type,
mech_type,
message_context,
status_string);
}
*/
import "C"
import (
"errors"
"fmt"
"strings"
)
// Constant values are specified for C-language bindings in RFC 2744.
/*
"""
These errors are encoded into the 32-bit GSS status code as follows:
MSB LSB
|------------------------------------------------------------|
| Calling Error | Routine Error | Supplementary Info |
|------------------------------------------------------------|
Bit 31 24 23 16 15 0
"""
Note that the first two fields hold integer consts, whereas Supplementary Info
is a bit-field.
*/
const (
shiftCALLING = 24
shiftROUTINE = 16
maskCALLING = 0xFF000000
maskROUTINE = 0x00FF0000
maskSUPPINFO = 0x0000FFFF
)
// Status values are returned by gssapi calls to indicate the result of a call.
// Declared according to: https://tools.ietf.org/html/rfc2743#page-17
const (
GSS_S_COMPLETE MajorStatus = 0
GSS_S_CALL_INACCESSIBLE_READ MajorStatus = 1 << shiftCALLING
GSS_S_CALL_INACCESSIBLE_WRITE = 2 << shiftCALLING
GSS_S_CALL_BAD_STRUCTURE = 3 << shiftCALLING
GSS_S_BAD_MECH MajorStatus = 1 << shiftROUTINE
GSS_S_BAD_NAME = 2 << shiftROUTINE
GSS_S_BAD_NAMETYPE = 3 << shiftROUTINE
GSS_S_BAD_BINDINGS = 4 << shiftROUTINE
GSS_S_BAD_STATUS = 5 << shiftROUTINE
GSS_S_BAD_MIC = 6 << shiftROUTINE
GSS_S_BAD_SIG = 6 << shiftROUTINE // duplication deliberate
GSS_S_NO_CRED = 7 << shiftROUTINE
GSS_S_NO_CONTEXT = 8 << shiftROUTINE
GSS_S_DEFECTIVE_TOKEN = 9 << shiftROUTINE
GSS_S_DEFECTIVE_CREDENTIAL = 10 << shiftROUTINE
GSS_S_CREDENTIALS_EXPIRED = 11 << shiftROUTINE
GSS_S_CONTEXT_EXPIRED = 12 << shiftROUTINE
GSS_S_FAILURE = 13 << shiftROUTINE
GSS_S_BAD_QOP = 14 << shiftROUTINE
GSS_S_UNAUTHORIZED = 15 << shiftROUTINE
GSS_S_UNAVAILABLE = 16 << shiftROUTINE
GSS_S_DUPLICATE_ELEMENT = 17 << shiftROUTINE
GSS_S_NAME_NOT_MN = 18 << shiftROUTINE
field_GSS_S_CONTINUE_NEEDED = 1 << 0
field_GSS_S_DUPLICATE_TOKEN = 1 << 1
field_GSS_S_OLD_TOKEN = 1 << 2
field_GSS_S_UNSEQ_TOKEN = 1 << 3
field_GSS_S_GAP_TOKEN = 1 << 4
)
// These are GSSAPI-defined:
// TODO: should MajorStatus be defined as C.OM_uint32?
type MajorStatus uint32
// CallingError is equivalent to C GSS_CALLING_ERROR() macro.
func (st MajorStatus) CallingError() MajorStatus {
return st & maskCALLING
}
// RoutineError is equivalent to C GSS_ROUTINE_ERROR() macro.
func (st MajorStatus) RoutineError() MajorStatus {
return st & maskROUTINE
}
// SupplementaryInfo is equivalent to C GSS_SUPPLEMENTARY_INFO() macro.
func (st MajorStatus) SupplementaryInfo() MajorStatus {
return st & maskSUPPINFO
}
// IsError is equivalent to C GSS_ERROR() macro. Not written as 'Error' because
// that's special in Go conventions. (i.e. conforming to error interface)
func (st MajorStatus) IsError() bool {
return st&(maskCALLING|maskROUTINE) != 0
}
// ContinueNeeded is equivalent to a C bitfield set test against the
// GSS_S_CONTINUE_NEEDED macro.
func (st MajorStatus) ContinueNeeded() bool {
return st&field_GSS_S_CONTINUE_NEEDED != 0
}
// DuplicateToken is equivalent to a C bitfield set test against the
// GSS_S_DUPLICATE_TOKEN macro.
func (st MajorStatus) DuplicateToken() bool {
return st&field_GSS_S_DUPLICATE_TOKEN != 0
}
// OldToken is equivalent to a C bitfield set test against the
// GSS_S_OLD_TOKEN macro.
func (st MajorStatus) OldToken() bool {
return st&field_GSS_S_OLD_TOKEN != 0
}
// UnseqToken is equivalent to a C bitfield set test against the
// GSS_S_UNSEQ_TOKEN macro.
func (st MajorStatus) UnseqToken() bool {
return st&field_GSS_S_UNSEQ_TOKEN != 0
}
// GapToken is equivalent to a C bitfield set test against the
// GSS_S_GAP_TOKEN macro.
func (st MajorStatus) GapToken() bool {
return st&field_GSS_S_GAP_TOKEN != 0
}
// Error is designed to serve both as an error, and as a general gssapi status
// container. If Major is GSS_S_FAILURE, then information will be in Minor.
// The GoError method will return a nil if it doesn't represent a real error.
type Error struct {
// gssapi lib binding, so that we can convert the results of an
// operation to a string for diagnosis.
*Lib
// Specified by gssapi
Major MajorStatus
// Mechanism-specific:
Minor C.OM_uint32
}
// MakeError creates a golang Error object from a gssapi major & minor status.
func (lib *Lib) MakeError(major, minor C.OM_uint32) *Error {
return &Error{
Lib: lib,
Major: MajorStatus(major),
Minor: minor,
}
}
// ErrContinueNeeded may be returned by InitSecContext or AcceptSecContext to
// indicate that another iteration is needed
var ErrContinueNeeded = errors.New("continue needed")
func (lib *Lib) stashLastStatus(major, minor C.OM_uint32) error {
lib.LastStatus = lib.MakeError(major, minor)
return lib.LastStatus.GoError()
}
// GoError returns an untyped error interface object.
func (e *Error) GoError() error {
if e.Major.IsError() {
return e
}
return 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")
}