3
// #include "sasl_windows.h"
13
type saslStepper interface {
14
Step(serverData []byte) (clientData []byte, done bool, err error)
18
type saslSession struct {
33
credHandle C.CredHandle
37
// Keep track of pointers we need to explicitly free
38
stringsToFree []*C.char
42
var initOnce sync.Once
45
rc := C.load_secur32_dll()
47
initError = fmt.Errorf("Error loading libraries: %v", rc)
51
func New(username, password, mechanism, service, host string) (saslStepper, error) {
53
ss := &saslSession{mech: mechanism, hasContext: 0, userPlusRealm: username}
57
if i := strings.Index(host, ":"); i >= 0 {
63
usernameComponents := strings.Split(username, "@")
64
if len(usernameComponents) < 2 {
65
return nil, fmt.Errorf("Username '%v' doesn't contain a realm!", username)
67
user := usernameComponents[0]
68
ss.domain = usernameComponents[1]
69
ss.target = fmt.Sprintf("%s/%s", ss.service, ss.host)
71
var status C.SECURITY_STATUS
72
// Step 0: call AcquireCredentialsHandle to get a nice SSPI CredHandle
73
if len(password) > 0 {
74
status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), ss.cstr(password), ss.cstr(ss.domain))
76
status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), nil, ss.cstr(ss.domain))
78
if status != C.SEC_E_OK {
80
return nil, fmt.Errorf("Couldn't create new SSPI client, error code %v", status)
85
func (ss *saslSession) cstr(s string) *C.char {
87
ss.stringsToFree = append(ss.stringsToFree, cstr)
91
func (ss *saslSession) Close() {
92
for _, cstr := range ss.stringsToFree {
93
C.free(unsafe.Pointer(cstr))
97
func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) {
100
return nil, false, fmt.Errorf("too many SSPI steps without authentication")
103
var bufferLength C.ULONG
104
if len(serverData) > 0 {
105
buffer = (C.PVOID)(unsafe.Pointer(&serverData[0]))
106
bufferLength = C.ULONG(len(serverData))
110
// Step 3: last bit of magic to use the correct server credentials
111
status = C.sspi_send_client_authz_id(&ss.context, &buffer, &bufferLength, ss.cstr(ss.userPlusRealm))
113
// Step 1 + Step 2: set up security context with the server and TGT
114
status = C.sspi_step(&ss.credHandle, ss.hasContext, &ss.context, &buffer, &bufferLength, ss.cstr(ss.target))
116
if buffer != C.PVOID(nil) {
117
defer C.free(unsafe.Pointer(buffer))
119
if status != C.SEC_E_OK && status != C.SEC_I_CONTINUE_NEEDED {
121
return nil, false, ss.handleSSPIErrorCode(status)
124
clientData = C.GoBytes(unsafe.Pointer(buffer), C.int(bufferLength))
125
if status == C.SEC_E_OK {
126
ss.authComplete = true
127
return clientData, true, nil
130
return clientData, false, nil
134
func (ss *saslSession) handleSSPIErrorCode(code C.int) error {
136
case code == C.SEC_E_TARGET_UNKNOWN:
137
return fmt.Errorf("Target %v@%v not found", ss.target, ss.domain)
139
return fmt.Errorf("Unknown error doing step %v, error code %v", ss.step, code)