github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/gopkg.in/mgo.v2/internal/sasl/sasl.go (about)

     1  // Package sasl is an implementation detail of the mgo package.
     2  //
     3  // This package is not meant to be used by itself.
     4  //
     5  
     6  // +build !windows
     7  
     8  package sasl
     9  
    10  // #cgo LDFLAGS: -lsasl2
    11  //
    12  // struct sasl_conn {};
    13  //
    14  // #include <stdlib.h>
    15  // #include <sasl/sasl.h>
    16  //
    17  // sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password);
    18  //
    19  import "C"
    20  
    21  import (
    22  	"fmt"
    23  	"strings"
    24  	"sync"
    25  	"unsafe"
    26  )
    27  
    28  type saslStepper interface {
    29  	Step(serverData []byte) (clientData []byte, done bool, err error)
    30  	Close()
    31  }
    32  
    33  type saslSession struct {
    34  	conn *C.sasl_conn_t
    35  	step int
    36  	mech string
    37  
    38  	cstrings  []*C.char
    39  	callbacks *C.sasl_callback_t
    40  }
    41  
    42  var initError error
    43  var initOnce sync.Once
    44  
    45  func initSASL() {
    46  	rc := C.sasl_client_init(nil)
    47  	if rc != C.SASL_OK {
    48  		initError = saslError(rc, nil, "cannot initialize SASL library")
    49  	}
    50  }
    51  
    52  func New(username, password, mechanism, service, host string) (saslStepper, error) {
    53  	initOnce.Do(initSASL)
    54  	if initError != nil {
    55  		return nil, initError
    56  	}
    57  
    58  	ss := &saslSession{mech: mechanism}
    59  	if service == "" {
    60  		service = "mongodb"
    61  	}
    62  	if i := strings.Index(host, ":"); i >= 0 {
    63  		host = host[:i]
    64  	}
    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)
    67  	if rc != C.SASL_OK {
    68  		ss.Close()
    69  		return nil, saslError(rc, nil, "cannot create new SASL client")
    70  	}
    71  	return ss, nil
    72  }
    73  
    74  func (ss *saslSession) cstr(s string) *C.char {
    75  	cstr := C.CString(s)
    76  	ss.cstrings = append(ss.cstrings, cstr)
    77  	return cstr
    78  }
    79  
    80  func (ss *saslSession) Close() {
    81  	for _, cstr := range ss.cstrings {
    82  		C.free(unsafe.Pointer(cstr))
    83  	}
    84  	ss.cstrings = nil
    85  
    86  	if ss.callbacks != nil {
    87  		C.free(unsafe.Pointer(ss.callbacks))
    88  	}
    89  
    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)
    96  }
    97  
    98  func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) {
    99  	ss.step++
   100  	if ss.step > 10 {
   101  		return nil, false, fmt.Errorf("too many SASL steps without authentication")
   102  	}
   103  	var cclientData *C.char
   104  	var cclientDataLen C.uint
   105  	var rc C.int
   106  	if ss.step == 1 {
   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)
   109  	} else {
   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))
   115  		}
   116  		rc = C.sasl_client_step(ss.conn, cserverData, cserverDataLen, nil, &cclientData, &cclientDataLen)
   117  	}
   118  	if cclientData != nil && cclientDataLen > 0 {
   119  		clientData = C.GoBytes(unsafe.Pointer(cclientData), C.int(cclientDataLen))
   120  	}
   121  	if rc == C.SASL_OK {
   122  		return clientData, true, nil
   123  	}
   124  	if rc == C.SASL_CONTINUE {
   125  		return clientData, false, nil
   126  	}
   127  	return nil, false, saslError(rc, ss.conn, "cannot establish SASL session")
   128  }
   129  
   130  func saslError(rc C.int, conn *C.sasl_conn_t, msg string) error {
   131  	var detail string
   132  	if conn == nil {
   133  		detail = C.GoString(C.sasl_errstring(rc, nil, nil))
   134  	} else {
   135  		detail = C.GoString(C.sasl_errdetail(conn))
   136  	}
   137  	return fmt.Errorf(msg + ": " + detail)
   138  }