// Copyright 2013-2015 Apcera Inc. All rights reserved. // +build darwin linux freebsd package gssapi /* #cgo linux LDFLAGS: -ldl #cgo freebsd pkg-config: heimdal-gssapi #include #include #include // Name-Types. These are standardized in the RFCs. The library requires that // a given name be usable for resolution, but it's typically a macro, there's // no guarantee about the name exported from the library. But since they're // static, and well-defined, we can just define them ourselves. // RFC2744-mandated values, mapping from as-near-as-possible to cut&paste const gss_OID_desc *_GSS_C_NT_USER_NAME = & (gss_OID_desc) { 10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01" }; const gss_OID_desc *_GSS_C_NT_MACHINE_UID_NAME = & (gss_OID_desc) { 10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02" }; const gss_OID_desc *_GSS_C_NT_STRING_UID_NAME = & (gss_OID_desc) { 10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03" }; const gss_OID_desc *_GSS_C_NT_HOSTBASED_SERVICE_X = & (gss_OID_desc) { 6, "\x2b\x06\x01\x05\x06\x02" }; const gss_OID_desc *_GSS_C_NT_HOSTBASED_SERVICE = & (gss_OID_desc) { 10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04" }; const gss_OID_desc *_GSS_C_NT_ANONYMOUS = & (gss_OID_desc) { 6, "\x2b\x06\x01\x05\x06\x03" }; // original had \01 const gss_OID_desc *_GSS_C_NT_EXPORT_NAME = & (gss_OID_desc) { 6, "\x2b\x06\x01\x05\x06\x04" }; // from gssapi_krb5.h: This name form shall be represented by the Object // Identifier {iso(1) member-body(2) United States(840) mit(113554) infosys(1) // gssapi(2) krb5(2) krb5_name(1)}. The recommended symbolic name for this // type is "GSS_KRB5_NT_PRINCIPAL_NAME". const gss_OID_desc *_GSS_KRB5_NT_PRINCIPAL_NAME = & (gss_OID_desc) { 10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01" }; // { 1 2 840 113554 1 2 2 2 } const gss_OID_desc *_GSS_KRB5_NT_PRINCIPAL = & (gss_OID_desc) { 10, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02" }; // known mech OIDs const gss_OID_desc *_GSS_MECH_KRB5 = & (gss_OID_desc) { 9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02" }; const gss_OID_desc *_GSS_MECH_KRB5_LEGACY = & (gss_OID_desc) { 9, "\x2A\x86\x48\x82\xF7\x12\x01\x02\x02" }; const gss_OID_desc *_GSS_MECH_KRB5_OLD = & (gss_OID_desc) { 5, "\x2B\x05\x01\x05\x02" }; const gss_OID_desc *_GSS_MECH_SPNEGO = & (gss_OID_desc) { 6, "\x2b\x06\x01\x05\x05\x02" }; const gss_OID_desc *_GSS_MECH_IAKERB = & (gss_OID_desc) { 6, "\x2b\x06\x01\x05\x02\x05" }; const gss_OID_desc *_GSS_MECH_NTLMSSP = & (gss_OID_desc) { 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" }; */ import "C" import ( "fmt" "os" "reflect" "runtime" "strings" "unsafe" ) // Values for Options.LoadDefault const ( MIT = iota Heimdal ) type Severity uint // Values for Options.Log severity indices const ( Emerg = Severity(iota) Alert Crit Err Warn Notice Info Debug MaxSeverity ) var severityNames = []string{ "Emerg", "Alert", "Crit", "Err", "Warn", "Notice", "Info", "Debug", } // String returns the string name of a log Severity. func (s Severity) String() string { if s >= MaxSeverity { return "" } return severityNames[s] } // Printer matches the log package, not fmt type Printer interface { Print(a ...interface{}) } // Options denote the options used to load a GSSAPI library. If a user supplies // a LibPath, we use that. Otherwise, based upon the default and the current OS, // we try to construct the library path. type Options struct { LibPath string Krb5Config string Krb5Ktname string LoadDefault int Printers []Printer `json:"-"` } // ftable fields will be initialized to the corresponding function pointers from // the GSSAPI library. They must be of form Fp_function_name (Capital 'F' so // that we can use reflect. type ftable struct { // buffer.go Fp_gss_release_buffer unsafe.Pointer Fp_gss_import_name unsafe.Pointer // context.go Fp_gss_init_sec_context unsafe.Pointer Fp_gss_accept_sec_context unsafe.Pointer Fp_gss_delete_sec_context unsafe.Pointer Fp_gss_process_context_token unsafe.Pointer Fp_gss_context_time unsafe.Pointer Fp_gss_inquire_context unsafe.Pointer Fp_gss_wrap_size_limit unsafe.Pointer Fp_gss_export_sec_context unsafe.Pointer Fp_gss_import_sec_context unsafe.Pointer // credential.go Fp_gss_acquire_cred unsafe.Pointer Fp_gss_add_cred unsafe.Pointer Fp_gss_inquire_cred unsafe.Pointer Fp_gss_inquire_cred_by_mech unsafe.Pointer Fp_gss_release_cred unsafe.Pointer // message.go Fp_gss_get_mic unsafe.Pointer Fp_gss_verify_mic unsafe.Pointer Fp_gss_wrap unsafe.Pointer Fp_gss_unwrap unsafe.Pointer // misc.go Fp_gss_indicate_mechs unsafe.Pointer // name.go Fp_gss_canonicalize_name unsafe.Pointer Fp_gss_compare_name unsafe.Pointer Fp_gss_display_name unsafe.Pointer Fp_gss_duplicate_name unsafe.Pointer Fp_gss_export_name unsafe.Pointer Fp_gss_inquire_mechs_for_name unsafe.Pointer Fp_gss_inquire_names_for_mech unsafe.Pointer Fp_gss_release_name unsafe.Pointer // oid_set.go Fp_gss_create_empty_oid_set unsafe.Pointer Fp_gss_add_oid_set_member unsafe.Pointer Fp_gss_release_oid_set unsafe.Pointer Fp_gss_test_oid_set_member unsafe.Pointer // status.go Fp_gss_display_status unsafe.Pointer // krb5_keytab.go -- where does this come from? // Fp_gsskrb5_register_acceptor_identity unsafe.Pointer } // constants are a number of constant initialized in initConstants. type constants struct { GSS_C_NO_BUFFER *Buffer GSS_C_NO_OID *OID GSS_C_NO_OID_SET *OIDSet GSS_C_NO_CONTEXT *CtxId GSS_C_NO_CREDENTIAL *CredId // when adding new OID constants also need to update OID.DebugString GSS_C_NT_USER_NAME *OID GSS_C_NT_MACHINE_UID_NAME *OID GSS_C_NT_STRING_UID_NAME *OID GSS_C_NT_HOSTBASED_SERVICE_X *OID GSS_C_NT_HOSTBASED_SERVICE *OID GSS_C_NT_ANONYMOUS *OID GSS_C_NT_EXPORT_NAME *OID GSS_KRB5_NT_PRINCIPAL_NAME *OID GSS_KRB5_NT_PRINCIPAL *OID GSS_MECH_KRB5 *OID GSS_MECH_KRB5_LEGACY *OID GSS_MECH_KRB5_OLD *OID GSS_MECH_SPNEGO *OID GSS_MECH_IAKERB *OID GSS_MECH_NTLMSSP *OID GSS_C_NO_CHANNEL_BINDINGS ChannelBindings // implicitly initialized as nil } // Lib encapsulates both the GSSAPI and the library dlopen()'d for it. The // handle represents the dynamically-linked gssapi library handle. type Lib struct { LastStatus *Error // Should contain a gssapi.Printer for each severity level to be // logged, up to gssapi.MaxSeverity items Printers []Printer handle unsafe.Pointer ftable constants } const ( fpPrefix = "Fp_" ) // Path returns the chosen gssapi library path that we're looking for. func (o *Options) Path() string { switch { case o.LibPath != "": return o.LibPath case o.LoadDefault == MIT: return appendOSExt("libgssapi_krb5") case o.LoadDefault == Heimdal: return appendOSExt("libgssapi") } return "" } // Load attempts to load a dynamically-linked gssapi library from the path // specified by the supplied Options. func Load(o *Options) (*Lib, error) { if o == nil { o = &Options{} } // We get the error in a separate call, so we need to lock OS thread runtime.LockOSThread() defer runtime.UnlockOSThread() lib := &Lib{ Printers: o.Printers, } if o.Krb5Config != "" { err := os.Setenv("KRB5_CONFIG", o.Krb5Config) if err != nil { return nil, err } } if o.Krb5Ktname != "" { err := os.Setenv("KRB5_KTNAME", o.Krb5Ktname) if err != nil { return nil, err } } path := o.Path() lib.Debug(fmt.Sprintf("Loading %q", path)) lib_cs := C.CString(path) defer C.free(unsafe.Pointer(lib_cs)) // we don't use RTLD_FIRST, it might be the case that the GSSAPI lib // delegates symbols to other libs it links against (eg, Kerberos) lib.handle = C.dlopen(lib_cs, C.RTLD_NOW|C.RTLD_LOCAL) if lib.handle == nil { return nil, fmt.Errorf("%s", C.GoString(C.dlerror())) } err := lib.populateFunctions() if err != nil { lib.Unload() return nil, err } lib.initConstants() return lib, nil } // Unload closes the handle to the dynamically-linked gssapi library. func (lib *Lib) Unload() error { if lib == nil || lib.handle == nil { return nil } runtime.LockOSThread() defer runtime.UnlockOSThread() i := C.dlclose(lib.handle) if i == -1 { return fmt.Errorf("%s", C.GoString(C.dlerror())) } lib.handle = nil return nil } func appendOSExt(path string) string { ext := ".so" if runtime.GOOS == "darwin" { ext = ".dylib" } if !strings.HasSuffix(path, ext) { path += ext } return path } // populateFunctions ranges over the library's ftable, initializing each // function inside. Assumes that the caller executes runtime.LockOSThread. func (lib *Lib) populateFunctions() error { libT := reflect.TypeOf(lib.ftable) functionsV := reflect.ValueOf(lib).Elem().FieldByName("ftable") n := libT.NumField() for i := 0; i < n; i++ { // Get the field name, and make sure it's an Fp_. f := libT.FieldByIndex([]int{i}) if !strings.HasPrefix(f.Name, fpPrefix) { return fmt.Errorf( "Unexpected: field %q does not start with %q", f.Name, fpPrefix) } // Resolve the symbol. cfname := C.CString(f.Name[len(fpPrefix):]) v := C.dlsym(lib.handle, cfname) C.free(unsafe.Pointer(cfname)) if v == nil { return fmt.Errorf("%s", C.GoString(C.dlerror())) } // Save the value into the struct functionsV.FieldByIndex([]int{i}).SetPointer(v) } return nil } // initConstants sets the initial values of a library's set of 'constants'. func (lib *Lib) initConstants() { lib.GSS_C_NO_BUFFER = &Buffer{ Lib: lib, // C_gss_buffer_t: C.GSS_C_NO_BUFFER, already nil // alloc: allocNone, already 0 } lib.GSS_C_NO_OID = lib.NewOID() lib.GSS_C_NO_OID_SET = lib.NewOIDSet() lib.GSS_C_NO_CONTEXT = lib.NewCtxId() lib.GSS_C_NO_CREDENTIAL = lib.NewCredId() lib.GSS_C_NT_USER_NAME = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_USER_NAME} lib.GSS_C_NT_MACHINE_UID_NAME = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_MACHINE_UID_NAME} lib.GSS_C_NT_STRING_UID_NAME = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_MACHINE_UID_NAME} lib.GSS_C_NT_HOSTBASED_SERVICE_X = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_HOSTBASED_SERVICE_X} lib.GSS_C_NT_HOSTBASED_SERVICE = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_HOSTBASED_SERVICE} lib.GSS_C_NT_ANONYMOUS = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_ANONYMOUS} lib.GSS_C_NT_EXPORT_NAME = &OID{Lib: lib, C_gss_OID: C._GSS_C_NT_EXPORT_NAME} lib.GSS_KRB5_NT_PRINCIPAL_NAME = &OID{Lib: lib, C_gss_OID: C._GSS_KRB5_NT_PRINCIPAL_NAME} lib.GSS_KRB5_NT_PRINCIPAL = &OID{Lib: lib, C_gss_OID: C._GSS_KRB5_NT_PRINCIPAL} lib.GSS_MECH_KRB5 = &OID{Lib: lib, C_gss_OID: C._GSS_MECH_KRB5} lib.GSS_MECH_KRB5_LEGACY = &OID{Lib: lib, C_gss_OID: C._GSS_MECH_KRB5_LEGACY} lib.GSS_MECH_KRB5_OLD = &OID{Lib: lib, C_gss_OID: C._GSS_MECH_KRB5_OLD} lib.GSS_MECH_SPNEGO = &OID{Lib: lib, C_gss_OID: C._GSS_MECH_SPNEGO} lib.GSS_MECH_IAKERB = &OID{Lib: lib, C_gss_OID: C._GSS_MECH_IAKERB} lib.GSS_MECH_NTLMSSP = &OID{Lib: lib, C_gss_OID: C._GSS_MECH_NTLMSSP} } // Print outputs a log line to the specified severity. func (lib *Lib) Print(level Severity, a ...interface{}) { if lib == nil || lib.Printers == nil || level >= Severity(len(lib.Printers)) { return } lib.Printers[level].Print(a...) } func (lib *Lib) Emerg(a ...interface{}) { lib.Print(Emerg, a...) } func (lib *Lib) Alert(a ...interface{}) { lib.Print(Alert, a...) } func (lib *Lib) Crit(a ...interface{}) { lib.Print(Crit, a...) } func (lib *Lib) Err(a ...interface{}) { lib.Print(Err, a...) } func (lib *Lib) Warn(a ...interface{}) { lib.Print(Warn, a...) } func (lib *Lib) Notice(a ...interface{}) { lib.Print(Notice, a...) } func (lib *Lib) Info(a ...interface{}) { lib.Print(Info, a...) } func (lib *Lib) Debug(a ...interface{}) { lib.Print(Debug, a...) }