1
2
3
4
5
6
7
8
9
10
11 package gssapi
12
13
20 import "C"
21 import (
22 "fmt"
23 "runtime"
24 "strings"
25 "unsafe"
26 )
27
28
29 func New(target, username, password string, passwordSet bool, props map[string]string) (*SaslClient, error) {
30 serviceName := "mongodb"
31
32 for key, value := range props {
33 switch strings.ToUpper(key) {
34 case "CANONICALIZE_HOST_NAME":
35 return nil, fmt.Errorf("CANONICALIZE_HOST_NAME is not supported when using gssapi on %s", runtime.GOOS)
36 case "SERVICE_REALM":
37 return nil, fmt.Errorf("SERVICE_REALM is not supported when using gssapi on %s", runtime.GOOS)
38 case "SERVICE_NAME":
39 serviceName = value
40 case "SERVICE_HOST":
41 target = value
42 default:
43 return nil, fmt.Errorf("unknown mechanism property %s", key)
44 }
45 }
46
47 servicePrincipalName := fmt.Sprintf("%s@%s", serviceName, target)
48
49 return &SaslClient{
50 servicePrincipalName: servicePrincipalName,
51 username: username,
52 password: password,
53 passwordSet: passwordSet,
54 }, nil
55 }
56
57 type SaslClient struct {
58 servicePrincipalName string
59 username string
60 password string
61 passwordSet bool
62
63
64 state C.gssapi_client_state
65 contextComplete bool
66 done bool
67 }
68
69 func (sc *SaslClient) Close() {
70 C.gssapi_client_destroy(&sc.state)
71 }
72
73 func (sc *SaslClient) Start() (string, []byte, error) {
74 const mechName = "GSSAPI"
75
76 cservicePrincipalName := C.CString(sc.servicePrincipalName)
77 defer C.free(unsafe.Pointer(cservicePrincipalName))
78 var cusername *C.char
79 var cpassword *C.char
80 if sc.username != "" {
81 cusername = C.CString(sc.username)
82 defer C.free(unsafe.Pointer(cusername))
83 if sc.passwordSet {
84 cpassword = C.CString(sc.password)
85 defer C.free(unsafe.Pointer(cpassword))
86 }
87 }
88 status := C.gssapi_client_init(&sc.state, cservicePrincipalName, cusername, cpassword)
89
90 if status != C.GSSAPI_OK {
91 return mechName, nil, sc.getError("unable to initialize client")
92 }
93
94 payload, err := sc.Next(nil)
95
96 return mechName, payload, err
97 }
98
99 func (sc *SaslClient) Next(challenge []byte) ([]byte, error) {
100
101 var buf unsafe.Pointer
102 var bufLen C.size_t
103 var outBuf unsafe.Pointer
104 var outBufLen C.size_t
105
106 if sc.contextComplete {
107 if sc.username == "" {
108 var cusername *C.char
109 status := C.gssapi_client_username(&sc.state, &cusername)
110 if status != C.GSSAPI_OK {
111 return nil, sc.getError("unable to acquire username")
112 }
113 defer C.free(unsafe.Pointer(cusername))
114 sc.username = C.GoString((*C.char)(unsafe.Pointer(cusername)))
115 }
116
117 bytes := append([]byte{1, 0, 0, 0}, []byte(sc.username)...)
118 buf = unsafe.Pointer(&bytes[0])
119 bufLen = C.size_t(len(bytes))
120 status := C.gssapi_client_wrap_msg(&sc.state, buf, bufLen, &outBuf, &outBufLen)
121 if status != C.GSSAPI_OK {
122 return nil, sc.getError("unable to wrap authz")
123 }
124
125 sc.done = true
126 } else {
127 if len(challenge) > 0 {
128 buf = unsafe.Pointer(&challenge[0])
129 bufLen = C.size_t(len(challenge))
130 }
131
132 status := C.gssapi_client_negotiate(&sc.state, buf, bufLen, &outBuf, &outBufLen)
133 switch status {
134 case C.GSSAPI_OK:
135 sc.contextComplete = true
136 case C.GSSAPI_CONTINUE:
137 default:
138 return nil, sc.getError("unable to negotiate with server")
139 }
140 }
141
142 if outBuf != nil {
143 defer C.free(outBuf)
144 }
145
146 return C.GoBytes(outBuf, C.int(outBufLen)), nil
147 }
148
149 func (sc *SaslClient) Completed() bool {
150 return sc.done
151 }
152
153 func (sc *SaslClient) getError(prefix string) error {
154 var desc *C.char
155
156 status := C.gssapi_error_desc(sc.state.maj_stat, sc.state.min_stat, &desc)
157 if status != C.GSSAPI_OK {
158 if desc != nil {
159 C.free(unsafe.Pointer(desc))
160 }
161
162 return fmt.Errorf("%s: (%v, %v)", prefix, sc.state.maj_stat, sc.state.min_stat)
163 }
164 defer C.free(unsafe.Pointer(desc))
165
166 return fmt.Errorf("%s: %v(%v,%v)", prefix, C.GoString(desc), int32(sc.state.maj_stat), int32(sc.state.min_stat))
167 }
168
View as plain text