-
Notifications
You must be signed in to change notification settings - Fork 25
/
lib.go
394 lines (328 loc) · 11.8 KB
/
lib.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
// 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 <gssapi/gssapi.h>
#include <dlfcn.h>
#include <stdlib.h>
// 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...) }