...

Source file src/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/gss.go

Documentation: go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  //go:build gssapi && (linux || darwin)
     8  // +build gssapi
     9  // +build linux darwin
    10  
    11  package gssapi
    12  
    13  /*
    14  #cgo linux CFLAGS: -DGOOS_linux
    15  #cgo linux LDFLAGS: -lgssapi_krb5 -lkrb5
    16  #cgo darwin CFLAGS: -DGOOS_darwin
    17  #cgo darwin LDFLAGS: -framework GSS
    18  #include "gss_wrapper.h"
    19  */
    20  import "C"
    21  import (
    22  	"fmt"
    23  	"runtime"
    24  	"strings"
    25  	"unsafe"
    26  )
    27  
    28  // New creates a new SaslClient. The target parameter should be a hostname with no port.
    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  	// state
    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