mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-30 22:11:07 +00:00 
			
		
		
		
	* Add single sign-on support via SSPI on Windows * Ensure plugins implement interface * Ensure plugins implement interface * Move functions used only by the SSPI auth method to sspi_windows.go * Field SSPISeparatorReplacement of AuthenticationForm should not be required via binding, as binding will insist the field is non-empty even if another login type is selected * Fix breaking of oauth authentication on download links. Do not create new session with SSPI authentication on download links. * Update documentation for the new 'SPNEGO with SSPI' login source * Mention in documentation that ROOT_URL should contain the FQDN of the server * Make sure that Contexter is not checking for active login sources when the ORM engine is not initialized (eg. when installing) * Always initialize and free SSO methods, even if they are not enabled, as a method can be activated while the app is running (from Authentication sources) * Add option in SSPIConfig for removing of domains from logon names * Update helper text for StripDomainNames option * Make sure handleSignIn() is called after a new user object is created by SSPI auth method * Remove default value from text of form field helper Co-Authored-By: Lauris BH <lauris@nix.lv> * Remove default value from text of form field helper Co-Authored-By: Lauris BH <lauris@nix.lv> * Remove default value from text of form field helper Co-Authored-By: Lauris BH <lauris@nix.lv> * Only make a query to the DB to check if SSPI is enabled on handlers that need that information for templates * Remove code duplication * Log errors in ActiveLoginSources Co-Authored-By: Lauris BH <lauris@nix.lv> * Revert suffix of randomly generated E-mails for Reverse proxy authentication Co-Authored-By: Lauris BH <lauris@nix.lv> * Revert unneeded white-space change in template Co-Authored-By: Lauris BH <lauris@nix.lv> * Add copyright comments at the top of new files * Use loopback name for randomly generated emails * Add locale tag for the SSPISeparatorReplacement field with proper casing * Revert casing of SSPISeparatorReplacement field in locale file, moving it up, next to other form fields * Update docs/content/doc/features/authentication.en-us.md Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Remove Priority() method and define the order in which SSO auth methods should be executed in one place * Log authenticated username only if it's not empty * Rephrase helper text for automatic creation of users * Return error if more than one active SSPI auth source is found * Change newUser() function to return error, letting caller log/handle the error * Move isPublicResource, isPublicPage and handleSignIn functions outside SSPI auth method to allow other SSO methods to reuse them if needed * Refactor initialization of the list containing SSO auth methods * Validate SSPI settings on POST * Change SSPI to only perform authentication on its own login page, API paths and download links. Leave Toggle middleware to redirect non authenticated users to login page * Make 'Default language' in SSPI config empty, unless changed by admin * Show error if admin tries to add a second authentication source of type SSPI * Simplify declaration of global variable * Rebuild gitgraph.js on Linux * Make sure config values containing only whitespace are not accepted
		
			
				
	
	
		
			312 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			312 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| package websspi
 | |
| 
 | |
| import (
 | |
| 	"syscall"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"golang.org/x/sys/windows"
 | |
| )
 | |
| 
 | |
| // secur32.dll
 | |
| 
 | |
| type SECURITY_STATUS syscall.Errno
 | |
| 
 | |
| const (
 | |
| 	SEC_E_OK = SECURITY_STATUS(0)
 | |
| 
 | |
| 	SEC_E_INCOMPLETE_MESSAGE          = SECURITY_STATUS(0x80090318)
 | |
| 	SEC_E_INSUFFICIENT_MEMORY         = SECURITY_STATUS(0x80090300)
 | |
| 	SEC_E_INTERNAL_ERROR              = SECURITY_STATUS(0x80090304)
 | |
| 	SEC_E_INVALID_HANDLE              = SECURITY_STATUS(0x80090301)
 | |
| 	SEC_E_INVALID_TOKEN               = SECURITY_STATUS(0x80090308)
 | |
| 	SEC_E_LOGON_DENIED                = SECURITY_STATUS(0x8009030C)
 | |
| 	SEC_E_NO_AUTHENTICATING_AUTHORITY = SECURITY_STATUS(0x80090311)
 | |
| 	SEC_E_NO_CREDENTIALS              = SECURITY_STATUS(0x8009030E)
 | |
| 	SEC_E_UNSUPPORTED_FUNCTION        = SECURITY_STATUS(0x80090302)
 | |
| 	SEC_I_COMPLETE_AND_CONTINUE       = SECURITY_STATUS(0x00090314)
 | |
| 	SEC_I_COMPLETE_NEEDED             = SECURITY_STATUS(0x00090313)
 | |
| 	SEC_I_CONTINUE_NEEDED             = SECURITY_STATUS(0x00090312)
 | |
| 	SEC_E_NOT_OWNER                   = SECURITY_STATUS(0x80090306)
 | |
| 	SEC_E_SECPKG_NOT_FOUND            = SECURITY_STATUS(0x80090305)
 | |
| 	SEC_E_UNKNOWN_CREDENTIALS         = SECURITY_STATUS(0x8009030D)
 | |
| 
 | |
| 	NEGOSSP_NAME         = "Negotiate"
 | |
| 	SECPKG_CRED_INBOUND  = 1
 | |
| 	SECURITY_NATIVE_DREP = 16
 | |
| 
 | |
| 	ASC_REQ_DELEGATE        = 1
 | |
| 	ASC_REQ_MUTUAL_AUTH     = 2
 | |
| 	ASC_REQ_REPLAY_DETECT   = 4
 | |
| 	ASC_REQ_SEQUENCE_DETECT = 8
 | |
| 	ASC_REQ_CONFIDENTIALITY = 16
 | |
| 	ASC_REQ_USE_SESSION_KEY = 32
 | |
| 	ASC_REQ_ALLOCATE_MEMORY = 256
 | |
| 	ASC_REQ_USE_DCE_STYLE   = 512
 | |
| 	ASC_REQ_DATAGRAM        = 1024
 | |
| 	ASC_REQ_CONNECTION      = 2048
 | |
| 	ASC_REQ_EXTENDED_ERROR  = 32768
 | |
| 	ASC_REQ_STREAM          = 65536
 | |
| 	ASC_REQ_INTEGRITY       = 131072
 | |
| 
 | |
| 	SECPKG_ATTR_SIZES            = 0
 | |
| 	SECPKG_ATTR_NAMES            = 1
 | |
| 	SECPKG_ATTR_LIFESPAN         = 2
 | |
| 	SECPKG_ATTR_DCE_INFO         = 3
 | |
| 	SECPKG_ATTR_STREAM_SIZES     = 4
 | |
| 	SECPKG_ATTR_KEY_INFO         = 5
 | |
| 	SECPKG_ATTR_AUTHORITY        = 6
 | |
| 	SECPKG_ATTR_PROTO_INFO       = 7
 | |
| 	SECPKG_ATTR_PASSWORD_EXPIRY  = 8
 | |
| 	SECPKG_ATTR_SESSION_KEY      = 9
 | |
| 	SECPKG_ATTR_PACKAGE_INFO     = 10
 | |
| 	SECPKG_ATTR_USER_FLAGS       = 11
 | |
| 	SECPKG_ATTR_NEGOTIATION_INFO = 12
 | |
| 	SECPKG_ATTR_NATIVE_NAMES     = 13
 | |
| 	SECPKG_ATTR_FLAGS            = 14
 | |
| 
 | |
| 	SECBUFFER_VERSION = 0
 | |
| 	SECBUFFER_TOKEN   = 2
 | |
| )
 | |
| 
 | |
| type CredHandle struct {
 | |
| 	Lower uintptr
 | |
| 	Upper uintptr
 | |
| }
 | |
| 
 | |
| type CtxtHandle struct {
 | |
| 	Lower uintptr
 | |
| 	Upper uintptr
 | |
| }
 | |
| 
 | |
| type SecBuffer struct {
 | |
| 	BufferSize uint32
 | |
| 	BufferType uint32
 | |
| 	Buffer     *byte
 | |
| }
 | |
| 
 | |
| type SecBufferDesc struct {
 | |
| 	Version      uint32
 | |
| 	BuffersCount uint32
 | |
| 	Buffers      *SecBuffer
 | |
| }
 | |
| 
 | |
| type LUID struct {
 | |
| 	LowPart  uint32
 | |
| 	HighPart int32
 | |
| }
 | |
| 
 | |
| type SecPkgContext_Names struct {
 | |
| 	UserName *uint16
 | |
| }
 | |
| 
 | |
| type SecPkgContext_Flags struct {
 | |
| 	Flags uint32
 | |
| }
 | |
| 
 | |
| // netapi32.dll
 | |
| 
 | |
| const (
 | |
| 	NERR_Success       = 0x0
 | |
| 	NERR_InternalError = 0x85C
 | |
| 	NERR_UserNotFound  = 0x8AD
 | |
| 
 | |
| 	ERROR_ACCESS_DENIED     = 0x5
 | |
| 	ERROR_BAD_NETPATH       = 0x35
 | |
| 	ERROR_INVALID_LEVEL     = 0x7C
 | |
| 	ERROR_INVALID_NAME      = 0x7B
 | |
| 	ERROR_MORE_DATA         = 0xEA
 | |
| 	ERROR_NOT_ENOUGH_MEMORY = 0x8
 | |
| 
 | |
| 	MAX_PREFERRED_LENGTH  = 0xFFFFFFFF
 | |
| 	MAX_GROUP_NAME_LENGTH = 256
 | |
| 
 | |
| 	SE_GROUP_MANDATORY          = 0x1
 | |
| 	SE_GROUP_ENABLED_BY_DEFAULT = 0x2
 | |
| 	SE_GROUP_ENABLED            = 0x4
 | |
| 	SE_GROUP_OWNER              = 0x8
 | |
| 	SE_GROUP_USE_FOR_DENY_ONLY  = 0x10
 | |
| 	SE_GROUP_INTEGRITY          = 0x20
 | |
| 	SE_GROUP_INTEGRITY_ENABLED  = 0x40
 | |
| 	SE_GROUP_LOGON_ID           = 0xC0000000
 | |
| 	SE_GROUP_RESOURCE           = 0x20000000
 | |
| )
 | |
| 
 | |
| type GroupUsersInfo0 struct {
 | |
| 	Grui0_name *uint16
 | |
| }
 | |
| 
 | |
| type GroupUsersInfo1 struct {
 | |
| 	Grui1_name       *uint16
 | |
| 	Grui1_attributes uint32
 | |
| }
 | |
| 
 | |
| // The API interface describes the Win32 functions used in this package and
 | |
| // its primary purpose is to allow replacing them with stub functions in unit tests.
 | |
| type API interface {
 | |
| 	AcquireCredentialsHandle(
 | |
| 		principal *uint16,
 | |
| 		_package *uint16,
 | |
| 		credentialUse uint32,
 | |
| 		logonID *LUID,
 | |
| 		authData *byte,
 | |
| 		getKeyFn uintptr,
 | |
| 		getKeyArgument uintptr,
 | |
| 		credHandle *CredHandle,
 | |
| 		expiry *syscall.Filetime,
 | |
| 	) SECURITY_STATUS
 | |
| 	AcceptSecurityContext(
 | |
| 		credential *CredHandle,
 | |
| 		context *CtxtHandle,
 | |
| 		input *SecBufferDesc,
 | |
| 		contextReq uint32,
 | |
| 		targDataRep uint32,
 | |
| 		newContext *CtxtHandle,
 | |
| 		output *SecBufferDesc,
 | |
| 		contextAttr *uint32,
 | |
| 		expiry *syscall.Filetime,
 | |
| 	) SECURITY_STATUS
 | |
| 	QueryContextAttributes(context *CtxtHandle, attribute uint32, buffer *byte) SECURITY_STATUS
 | |
| 	DeleteSecurityContext(context *CtxtHandle) SECURITY_STATUS
 | |
| 	FreeContextBuffer(buffer *byte) SECURITY_STATUS
 | |
| 	FreeCredentialsHandle(handle *CredHandle) SECURITY_STATUS
 | |
| 	NetUserGetGroups(
 | |
| 		serverName *uint16,
 | |
| 		userName *uint16,
 | |
| 		level uint32,
 | |
| 		buf **byte,
 | |
| 		prefmaxlen uint32,
 | |
| 		entriesread *uint32,
 | |
| 		totalentries *uint32,
 | |
| 	) (neterr error)
 | |
| 	NetApiBufferFree(buf *byte) (neterr error)
 | |
| }
 | |
| 
 | |
| // Win32 implements the API interface by calling the relevant system functions
 | |
| // from secur32.dll and netapi32.dll
 | |
| type Win32 struct{}
 | |
| 
 | |
| var (
 | |
| 	secur32dll  = windows.NewLazySystemDLL("secur32.dll")
 | |
| 	netapi32dll = windows.NewLazySystemDLL("netapi32.dll")
 | |
| 
 | |
| 	procAcquireCredentialsHandleW = secur32dll.NewProc("AcquireCredentialsHandleW")
 | |
| 	procAcceptSecurityContext     = secur32dll.NewProc("AcceptSecurityContext")
 | |
| 	procQueryContextAttributesW   = secur32dll.NewProc("QueryContextAttributesW")
 | |
| 	procDeleteSecurityContext     = secur32dll.NewProc("DeleteSecurityContext")
 | |
| 	procFreeContextBuffer         = secur32dll.NewProc("FreeContextBuffer")
 | |
| 	procFreeCredentialsHandle     = secur32dll.NewProc("FreeCredentialsHandle")
 | |
| 	procNetUserGetGroups          = netapi32dll.NewProc("NetUserGetGroups")
 | |
| )
 | |
| 
 | |
| func (w *Win32) AcquireCredentialsHandle(
 | |
| 	principal *uint16,
 | |
| 	_package *uint16,
 | |
| 	credentialUse uint32,
 | |
| 	logonId *LUID,
 | |
| 	authData *byte,
 | |
| 	getKeyFn uintptr,
 | |
| 	getKeyArgument uintptr,
 | |
| 	credHandle *CredHandle,
 | |
| 	expiry *syscall.Filetime,
 | |
| ) SECURITY_STATUS {
 | |
| 	r1, _, _ := syscall.Syscall9(
 | |
| 		procAcquireCredentialsHandleW.Addr(), 9,
 | |
| 		uintptr(unsafe.Pointer(principal)),
 | |
| 		uintptr(unsafe.Pointer(_package)),
 | |
| 		uintptr(credentialUse),
 | |
| 		uintptr(unsafe.Pointer(logonId)),
 | |
| 		uintptr(unsafe.Pointer(authData)),
 | |
| 		uintptr(getKeyFn),
 | |
| 		uintptr(getKeyArgument),
 | |
| 		uintptr(unsafe.Pointer(credHandle)),
 | |
| 		uintptr(unsafe.Pointer(expiry)),
 | |
| 	)
 | |
| 	return SECURITY_STATUS(r1)
 | |
| }
 | |
| 
 | |
| func (w *Win32) AcceptSecurityContext(
 | |
| 	credential *CredHandle,
 | |
| 	context *CtxtHandle,
 | |
| 	input *SecBufferDesc,
 | |
| 	contextReq uint32,
 | |
| 	targDataRep uint32,
 | |
| 	newContext *CtxtHandle,
 | |
| 	output *SecBufferDesc,
 | |
| 	contextAttr *uint32,
 | |
| 	expiry *syscall.Filetime,
 | |
| ) SECURITY_STATUS {
 | |
| 	r1, _, _ := syscall.Syscall9(
 | |
| 		procAcceptSecurityContext.Addr(), 9,
 | |
| 		uintptr(unsafe.Pointer(credential)),
 | |
| 		uintptr(unsafe.Pointer(context)),
 | |
| 		uintptr(unsafe.Pointer(input)),
 | |
| 		uintptr(contextReq),
 | |
| 		uintptr(targDataRep),
 | |
| 		uintptr(unsafe.Pointer(newContext)),
 | |
| 		uintptr(unsafe.Pointer(output)),
 | |
| 		uintptr(unsafe.Pointer(contextAttr)),
 | |
| 		uintptr(unsafe.Pointer(expiry)),
 | |
| 	)
 | |
| 	return SECURITY_STATUS(r1)
 | |
| }
 | |
| 
 | |
| func (w *Win32) QueryContextAttributes(
 | |
| 	context *CtxtHandle,
 | |
| 	attribute uint32,
 | |
| 	buffer *byte,
 | |
| ) SECURITY_STATUS {
 | |
| 	r1, _, _ := syscall.Syscall(
 | |
| 		procQueryContextAttributesW.Addr(), 3,
 | |
| 		uintptr(unsafe.Pointer(context)),
 | |
| 		uintptr(attribute),
 | |
| 		uintptr(unsafe.Pointer(buffer)),
 | |
| 	)
 | |
| 	return SECURITY_STATUS(r1)
 | |
| }
 | |
| 
 | |
| func (w *Win32) DeleteSecurityContext(context *CtxtHandle) SECURITY_STATUS {
 | |
| 	r1, _, _ := syscall.Syscall(
 | |
| 		procDeleteSecurityContext.Addr(), 1,
 | |
| 		uintptr(unsafe.Pointer(context)),
 | |
| 		0, 0,
 | |
| 	)
 | |
| 	return SECURITY_STATUS(r1)
 | |
| }
 | |
| 
 | |
| func (w *Win32) FreeContextBuffer(buffer *byte) SECURITY_STATUS {
 | |
| 	r1, _, _ := syscall.Syscall(
 | |
| 		procFreeContextBuffer.Addr(), 1,
 | |
| 		uintptr(unsafe.Pointer(buffer)),
 | |
| 		0, 0,
 | |
| 	)
 | |
| 	return SECURITY_STATUS(r1)
 | |
| }
 | |
| 
 | |
| func (w *Win32) FreeCredentialsHandle(handle *CredHandle) SECURITY_STATUS {
 | |
| 	r1, _, _ := syscall.Syscall(
 | |
| 		procFreeCredentialsHandle.Addr(), 1,
 | |
| 		uintptr(unsafe.Pointer(handle)),
 | |
| 		0, 0,
 | |
| 	)
 | |
| 	return SECURITY_STATUS(r1)
 | |
| }
 | |
| 
 | |
| func (w *Win32) NetUserGetGroups(
 | |
| 	serverName *uint16,
 | |
| 	userName *uint16,
 | |
| 	level uint32,
 | |
| 	buf **byte,
 | |
| 	prefmaxlen uint32,
 | |
| 	entriesread *uint32,
 | |
| 	totalentries *uint32,
 | |
| ) (neterr error) {
 | |
| 	r0, _, _ := syscall.Syscall9(procNetUserGetGroups.Addr(), 7, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), uintptr(prefmaxlen), uintptr(unsafe.Pointer(entriesread)), uintptr(unsafe.Pointer(totalentries)), 0, 0)
 | |
| 	if r0 != 0 {
 | |
| 		neterr = syscall.Errno(r0)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (w *Win32) NetApiBufferFree(buf *byte) (neterr error) {
 | |
| 	return syscall.NetApiBufferFree(buf)
 | |
| }
 |