1
// Package sasl is an implementation detail of the mgo package.
3
// This package is not meant to be used by itself.
10
// #cgo LDFLAGS: -lsasl2
12
// struct sasl_conn {};
14
// #include <stdlib.h>
15
// #include <sasl/sasl.h>
17
// sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password);
28
type saslStepper interface {
29
Step(serverData []byte) (clientData []byte, done bool, err error)
33
type saslSession struct {
39
callbacks *C.sasl_callback_t
43
var initOnce sync.Once
46
rc := C.sasl_client_init(nil)
48
initError = saslError(rc, nil, "cannot initialize SASL library")
52
func New(username, password, mechanism, service, host string) (saslStepper, error) {
58
ss := &saslSession{mech: mechanism}
62
if i := strings.Index(host, ":"); i >= 0 {
65
ss.callbacks = C.mgo_sasl_callbacks(ss.cstr(username), ss.cstr(password))
66
rc := C.sasl_client_new(ss.cstr(service), ss.cstr(host), nil, nil, ss.callbacks, 0, &ss.conn)
69
return nil, saslError(rc, nil, "cannot create new SASL client")
74
func (ss *saslSession) cstr(s string) *C.char {
76
ss.cstrings = append(ss.cstrings, cstr)
80
func (ss *saslSession) Close() {
81
for _, cstr := range ss.cstrings {
82
C.free(unsafe.Pointer(cstr))
86
if ss.callbacks != nil {
87
C.free(unsafe.Pointer(ss.callbacks))
90
// The documentation of SASL dispose makes it clear that this should only
91
// be done when the connection is done, not when the authentication phase
92
// is done, because an encryption layer may have been negotiated.
93
// Even then, we'll do this for now, because it's simpler and prevents
94
// keeping track of this state for every socket. If it breaks, we'll fix it.
95
C.sasl_dispose(&ss.conn)
98
func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) {
101
return nil, false, fmt.Errorf("too many SASL steps without authentication")
103
var cclientData *C.char
104
var cclientDataLen C.uint
107
var mechanism *C.char // ignored - must match cred
108
rc = C.sasl_client_start(ss.conn, ss.cstr(ss.mech), nil, &cclientData, &cclientDataLen, &mechanism)
110
var cserverData *C.char
111
var cserverDataLen C.uint
112
if len(serverData) > 0 {
113
cserverData = (*C.char)(unsafe.Pointer(&serverData[0]))
114
cserverDataLen = C.uint(len(serverData))
116
rc = C.sasl_client_step(ss.conn, cserverData, cserverDataLen, nil, &cclientData, &cclientDataLen)
118
if cclientData != nil && cclientDataLen > 0 {
119
clientData = C.GoBytes(unsafe.Pointer(cclientData), C.int(cclientDataLen))
122
return clientData, true, nil
124
if rc == C.SASL_CONTINUE {
125
return clientData, false, nil
127
return nil, false, saslError(rc, ss.conn, "cannot establish SASL session")
130
func saslError(rc C.int, conn *C.sasl_conn_t, msg string) error {
133
detail = C.GoString(C.sasl_errstring(rc, nil, nil))
135
detail = C.GoString(C.sasl_errdetail(conn))
137
return fmt.Errorf(msg + ": " + detail)