...

Source file src/go.mongodb.org/mongo-driver/x/mongo/driver/auth/internal/gssapi/sspi.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 && windows
     8  // +build gssapi,windows
     9  
    10  package gssapi
    11  
    12  // #include "sspi_wrapper.h"
    13  import "C"
    14  import (
    15  	"fmt"
    16  	"net"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  	"unsafe"
    21  )
    22  
    23  // New creates a new SaslClient. The target parameter should be a hostname with no port.
    24  func New(target, username, password string, passwordSet bool, props map[string]string) (*SaslClient, error) {
    25  	initOnce.Do(initSSPI)
    26  	if initError != nil {
    27  		return nil, initError
    28  	}
    29  
    30  	var err error
    31  	serviceName := "mongodb"
    32  	serviceRealm := ""
    33  	canonicalizeHostName := false
    34  	var serviceHostSet bool
    35  
    36  	for key, value := range props {
    37  		switch strings.ToUpper(key) {
    38  		case "CANONICALIZE_HOST_NAME":
    39  			canonicalizeHostName, err = strconv.ParseBool(value)
    40  			if err != nil {
    41  				return nil, fmt.Errorf("%s must be a boolean (true, false, 0, 1) but got '%s'", key, value)
    42  			}
    43  
    44  		case "SERVICE_REALM":
    45  			serviceRealm = value
    46  		case "SERVICE_NAME":
    47  			serviceName = value
    48  		case "SERVICE_HOST":
    49  			serviceHostSet = true
    50  			target = value
    51  		}
    52  	}
    53  
    54  	if canonicalizeHostName {
    55  		// Should not canonicalize the SERVICE_HOST
    56  		if serviceHostSet {
    57  			return nil, fmt.Errorf("CANONICALIZE_HOST_NAME and SERVICE_HOST canonot both be specified")
    58  		}
    59  
    60  		names, err := net.LookupAddr(target)
    61  		if err != nil || len(names) == 0 {
    62  			return nil, fmt.Errorf("unable to canonicalize hostname: %s", err)
    63  		}
    64  		target = names[0]
    65  		if target[len(target)-1] == '.' {
    66  			target = target[:len(target)-1]
    67  		}
    68  	}
    69  
    70  	servicePrincipalName := fmt.Sprintf("%s/%s", serviceName, target)
    71  	if serviceRealm != "" {
    72  		servicePrincipalName += "@" + serviceRealm
    73  	}
    74  
    75  	return &SaslClient{
    76  		servicePrincipalName: servicePrincipalName,
    77  		username:             username,
    78  		password:             password,
    79  		passwordSet:          passwordSet,
    80  	}, nil
    81  }
    82  
    83  type SaslClient struct {
    84  	servicePrincipalName string
    85  	username             string
    86  	password             string
    87  	passwordSet          bool
    88  
    89  	// state
    90  	state           C.sspi_client_state
    91  	contextComplete bool
    92  	done            bool
    93  }
    94  
    95  func (sc *SaslClient) Close() {
    96  	C.sspi_client_destroy(&sc.state)
    97  }
    98  
    99  func (sc *SaslClient) Start() (string, []byte, error) {
   100  	const mechName = "GSSAPI"
   101  
   102  	var cusername *C.char
   103  	var cpassword *C.char
   104  	if sc.username != "" {
   105  		cusername = C.CString(sc.username)
   106  		defer C.free(unsafe.Pointer(cusername))
   107  		if sc.passwordSet {
   108  			cpassword = C.CString(sc.password)
   109  			defer C.free(unsafe.Pointer(cpassword))
   110  		}
   111  	}
   112  	status := C.sspi_client_init(&sc.state, cusername, cpassword)
   113  
   114  	if status != C.SSPI_OK {
   115  		return mechName, nil, sc.getError("unable to initialize client")
   116  	}
   117  
   118  	payload, err := sc.Next(nil)
   119  
   120  	return mechName, payload, err
   121  }
   122  
   123  func (sc *SaslClient) Next(challenge []byte) ([]byte, error) {
   124  
   125  	var outBuf C.PVOID
   126  	var outBufLen C.ULONG
   127  
   128  	if sc.contextComplete {
   129  		if sc.username == "" {
   130  			var cusername *C.char
   131  			status := C.sspi_client_username(&sc.state, &cusername)
   132  			if status != C.SSPI_OK {
   133  				return nil, sc.getError("unable to acquire username")
   134  			}
   135  			defer C.free(unsafe.Pointer(cusername))
   136  			sc.username = C.GoString((*C.char)(unsafe.Pointer(cusername)))
   137  		}
   138  
   139  		bytes := append([]byte{1, 0, 0, 0}, []byte(sc.username)...)
   140  		buf := (C.PVOID)(unsafe.Pointer(&bytes[0]))
   141  		bufLen := C.ULONG(len(bytes))
   142  		status := C.sspi_client_wrap_msg(&sc.state, buf, bufLen, &outBuf, &outBufLen)
   143  		if status != C.SSPI_OK {
   144  			return nil, sc.getError("unable to wrap authz")
   145  		}
   146  
   147  		sc.done = true
   148  	} else {
   149  		var buf C.PVOID
   150  		var bufLen C.ULONG
   151  		if len(challenge) > 0 {
   152  			buf = (C.PVOID)(unsafe.Pointer(&challenge[0]))
   153  			bufLen = C.ULONG(len(challenge))
   154  		}
   155  		cservicePrincipalName := C.CString(sc.servicePrincipalName)
   156  		defer C.free(unsafe.Pointer(cservicePrincipalName))
   157  
   158  		status := C.sspi_client_negotiate(&sc.state, cservicePrincipalName, buf, bufLen, &outBuf, &outBufLen)
   159  		switch status {
   160  		case C.SSPI_OK:
   161  			sc.contextComplete = true
   162  		case C.SSPI_CONTINUE:
   163  		default:
   164  			return nil, sc.getError("unable to negotiate with server")
   165  		}
   166  	}
   167  
   168  	if outBuf != C.PVOID(nil) {
   169  		defer C.free(unsafe.Pointer(outBuf))
   170  	}
   171  
   172  	return C.GoBytes(unsafe.Pointer(outBuf), C.int(outBufLen)), nil
   173  }
   174  
   175  func (sc *SaslClient) Completed() bool {
   176  	return sc.done
   177  }
   178  
   179  func (sc *SaslClient) getError(prefix string) error {
   180  	return getError(prefix, sc.state.status)
   181  }
   182  
   183  var initOnce sync.Once
   184  var initError error
   185  
   186  func initSSPI() {
   187  	rc := C.sspi_init()
   188  	if rc != 0 {
   189  		initError = fmt.Errorf("error initializing sspi: %v", rc)
   190  	}
   191  }
   192  
   193  func getError(prefix string, status C.SECURITY_STATUS) error {
   194  	var s string
   195  	switch status {
   196  	case C.SEC_E_ALGORITHM_MISMATCH:
   197  		s = "The client and server cannot communicate because they do not possess a common algorithm."
   198  	case C.SEC_E_BAD_BINDINGS:
   199  		s = "The SSPI channel bindings supplied by the client are incorrect."
   200  	case C.SEC_E_BAD_PKGID:
   201  		s = "The requested package identifier does not exist."
   202  	case C.SEC_E_BUFFER_TOO_SMALL:
   203  		s = "The buffers supplied to the function are not large enough to contain the information."
   204  	case C.SEC_E_CANNOT_INSTALL:
   205  		s = "The security package cannot initialize successfully and should not be installed."
   206  	case C.SEC_E_CANNOT_PACK:
   207  		s = "The package is unable to pack the context."
   208  	case C.SEC_E_CERT_EXPIRED:
   209  		s = "The received certificate has expired."
   210  	case C.SEC_E_CERT_UNKNOWN:
   211  		s = "An unknown error occurred while processing the certificate."
   212  	case C.SEC_E_CERT_WRONG_USAGE:
   213  		s = "The certificate is not valid for the requested usage."
   214  	case C.SEC_E_CONTEXT_EXPIRED:
   215  		s = "The application is referencing a context that has already been closed. A properly written application should not receive this error."
   216  	case C.SEC_E_CROSSREALM_DELEGATION_FAILURE:
   217  		s = "The server attempted to make a Kerberos-constrained delegation request for a target outside the server's realm."
   218  	case C.SEC_E_CRYPTO_SYSTEM_INVALID:
   219  		s = "The cryptographic system or checksum function is not valid because a required function is unavailable."
   220  	case C.SEC_E_DECRYPT_FAILURE:
   221  		s = "The specified data could not be decrypted."
   222  	case C.SEC_E_DELEGATION_REQUIRED:
   223  		s = "The requested operation cannot be completed. The computer must be trusted for delegation"
   224  	case C.SEC_E_DOWNGRADE_DETECTED:
   225  		s = "The system detected a possible attempt to compromise security. Verify that the server that authenticated you can be contacted."
   226  	case C.SEC_E_ENCRYPT_FAILURE:
   227  		s = "The specified data could not be encrypted."
   228  	case C.SEC_E_ILLEGAL_MESSAGE:
   229  		s = "The message received was unexpected or badly formatted."
   230  	case C.SEC_E_INCOMPLETE_CREDENTIALS:
   231  		s = "The credentials supplied were not complete and could not be verified. The context could not be initialized."
   232  	case C.SEC_E_INCOMPLETE_MESSAGE:
   233  		s = "The message supplied was incomplete. The signature was not verified."
   234  	case C.SEC_E_INSUFFICIENT_MEMORY:
   235  		s = "Not enough memory is available to complete the request."
   236  	case C.SEC_E_INTERNAL_ERROR:
   237  		s = "An error occurred that did not map to an SSPI error code."
   238  	case C.SEC_E_INVALID_HANDLE:
   239  		s = "The handle passed to the function is not valid."
   240  	case C.SEC_E_INVALID_TOKEN:
   241  		s = "The token passed to the function is not valid."
   242  	case C.SEC_E_ISSUING_CA_UNTRUSTED:
   243  		s = "An untrusted certification authority (CA) was detected while processing the smart card certificate used for authentication."
   244  	case C.SEC_E_ISSUING_CA_UNTRUSTED_KDC:
   245  		s = "An untrusted CA was detected while processing the domain controller certificate used for authentication. The system event log contains additional information."
   246  	case C.SEC_E_KDC_CERT_EXPIRED:
   247  		s = "The domain controller certificate used for smart card logon has expired."
   248  	case C.SEC_E_KDC_CERT_REVOKED:
   249  		s = "The domain controller certificate used for smart card logon has been revoked."
   250  	case C.SEC_E_KDC_INVALID_REQUEST:
   251  		s = "A request that is not valid was sent to the KDC."
   252  	case C.SEC_E_KDC_UNABLE_TO_REFER:
   253  		s = "The KDC was unable to generate a referral for the service requested."
   254  	case C.SEC_E_KDC_UNKNOWN_ETYPE:
   255  		s = "The requested encryption type is not supported by the KDC."
   256  	case C.SEC_E_LOGON_DENIED:
   257  		s = "The logon has been denied"
   258  	case C.SEC_E_MAX_REFERRALS_EXCEEDED:
   259  		s = "The number of maximum ticket referrals has been exceeded."
   260  	case C.SEC_E_MESSAGE_ALTERED:
   261  		s = "The message supplied for verification has been altered."
   262  	case C.SEC_E_MULTIPLE_ACCOUNTS:
   263  		s = "The received certificate was mapped to multiple accounts."
   264  	case C.SEC_E_MUST_BE_KDC:
   265  		s = "The local computer must be a Kerberos domain controller (KDC)"
   266  	case C.SEC_E_NO_AUTHENTICATING_AUTHORITY:
   267  		s = "No authority could be contacted for authentication."
   268  	case C.SEC_E_NO_CREDENTIALS:
   269  		s = "No credentials are available."
   270  	case C.SEC_E_NO_IMPERSONATION:
   271  		s = "No impersonation is allowed for this context."
   272  	case C.SEC_E_NO_IP_ADDRESSES:
   273  		s = "Unable to accomplish the requested task because the local computer does not have any IP addresses."
   274  	case C.SEC_E_NO_KERB_KEY:
   275  		s = "No Kerberos key was found."
   276  	case C.SEC_E_NO_PA_DATA:
   277  		s = "Policy administrator (PA) data is needed to determine the encryption type"
   278  	case C.SEC_E_NO_S4U_PROT_SUPPORT:
   279  		s = "The Kerberos subsystem encountered an error. A service for user protocol request was made against a domain controller which does not support service for a user."
   280  	case C.SEC_E_NO_TGT_REPLY:
   281  		s = "The client is trying to negotiate a context and the server requires a user-to-user connection"
   282  	case C.SEC_E_NOT_OWNER:
   283  		s = "The caller of the function does not own the credentials."
   284  	case C.SEC_E_OK:
   285  		s = "The operation completed successfully."
   286  	case C.SEC_E_OUT_OF_SEQUENCE:
   287  		s = "The message supplied for verification is out of sequence."
   288  	case C.SEC_E_PKINIT_CLIENT_FAILURE:
   289  		s = "The smart card certificate used for authentication is not trusted."
   290  	case C.SEC_E_PKINIT_NAME_MISMATCH:
   291  		s = "The client certificate does not contain a valid UPN or does not match the client name in the logon request."
   292  	case C.SEC_E_QOP_NOT_SUPPORTED:
   293  		s = "The quality of protection attribute is not supported by this package."
   294  	case C.SEC_E_REVOCATION_OFFLINE_C:
   295  		s = "The revocation status of the smart card certificate used for authentication could not be determined."
   296  	case C.SEC_E_REVOCATION_OFFLINE_KDC:
   297  		s = "The revocation status of the domain controller certificate used for smart card authentication could not be determined. The system event log contains additional information."
   298  	case C.SEC_E_SECPKG_NOT_FOUND:
   299  		s = "The security package was not recognized."
   300  	case C.SEC_E_SECURITY_QOS_FAILED:
   301  		s = "The security context could not be established due to a failure in the requested quality of service (for example"
   302  	case C.SEC_E_SHUTDOWN_IN_PROGRESS:
   303  		s = "A system shutdown is in progress."
   304  	case C.SEC_E_SMARTCARD_CERT_EXPIRED:
   305  		s = "The smart card certificate used for authentication has expired."
   306  	case C.SEC_E_SMARTCARD_CERT_REVOKED:
   307  		s = "The smart card certificate used for authentication has been revoked. Additional information may exist in the event log."
   308  	case C.SEC_E_SMARTCARD_LOGON_REQUIRED:
   309  		s = "Smart card logon is required and was not used."
   310  	case C.SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
   311  		s = "The other end of the security negotiation requires strong cryptography"
   312  	case C.SEC_E_TARGET_UNKNOWN:
   313  		s = "The target was not recognized."
   314  	case C.SEC_E_TIME_SKEW:
   315  		s = "The clocks on the client and server computers do not match."
   316  	case C.SEC_E_TOO_MANY_PRINCIPALS:
   317  		s = "The KDC reply contained more than one principal name."
   318  	case C.SEC_E_UNFINISHED_CONTEXT_DELETED:
   319  		s = "A security context was deleted before the context was completed. This is considered a logon failure."
   320  	case C.SEC_E_UNKNOWN_CREDENTIALS:
   321  		s = "The credentials provided were not recognized."
   322  	case C.SEC_E_UNSUPPORTED_FUNCTION:
   323  		s = "The requested function is not supported."
   324  	case C.SEC_E_UNSUPPORTED_PREAUTH:
   325  		s = "An unsupported preauthentication mechanism was presented to the Kerberos package."
   326  	case C.SEC_E_UNTRUSTED_ROOT:
   327  		s = "The certificate chain was issued by an authority that is not trusted."
   328  	case C.SEC_E_WRONG_CREDENTIAL_HANDLE:
   329  		s = "The supplied credential handle does not match the credential associated with the security context."
   330  	case C.SEC_E_WRONG_PRINCIPAL:
   331  		s = "The target principal name is incorrect."
   332  	case C.SEC_I_COMPLETE_AND_CONTINUE:
   333  		s = "The function completed successfully"
   334  	case C.SEC_I_COMPLETE_NEEDED:
   335  		s = "The function completed successfully"
   336  	case C.SEC_I_CONTEXT_EXPIRED:
   337  		s = "The message sender has finished using the connection and has initiated a shutdown. For information about initiating or recognizing a shutdown"
   338  	case C.SEC_I_CONTINUE_NEEDED:
   339  		s = "The function completed successfully"
   340  	case C.SEC_I_INCOMPLETE_CREDENTIALS:
   341  		s = "The credentials supplied were not complete and could not be verified. Additional information can be returned from the context."
   342  	case C.SEC_I_LOCAL_LOGON:
   343  		s = "The logon was completed"
   344  	case C.SEC_I_NO_LSA_CONTEXT:
   345  		s = "There is no LSA mode context associated with this context."
   346  	case C.SEC_I_RENEGOTIATE:
   347  		s = "The context data must be renegotiated with the peer."
   348  	default:
   349  		return fmt.Errorf("%s: 0x%x", prefix, uint32(status))
   350  	}
   351  
   352  	return fmt.Errorf("%s: %s(0x%x)", prefix, s, uint32(status))
   353  }
   354  

View as plain text