package mssql import ( "fmt" "strings" "syscall" "unsafe" ) var ( secur32_dll = syscall.NewLazyDLL("secur32.dll") initSecurityInterface = secur32_dll.NewProc("InitSecurityInterfaceW") sec_fn *SecurityFunctionTable ) func init() { ptr, _, _ := initSecurityInterface.Call() sec_fn = (*SecurityFunctionTable)(unsafe.Pointer(ptr)) } const ( SEC_E_OK = 0 SECPKG_CRED_OUTBOUND = 2 SEC_WINNT_AUTH_IDENTITY_UNICODE = 2 ISC_REQ_DELEGATE = 0x00000001 ISC_REQ_REPLAY_DETECT = 0x00000004 ISC_REQ_SEQUENCE_DETECT = 0x00000008 ISC_REQ_CONFIDENTIALITY = 0x00000010 ISC_REQ_CONNECTION = 0x00000800 SECURITY_NETWORK_DREP = 0 SEC_I_CONTINUE_NEEDED = 0x00090312 SEC_I_COMPLETE_NEEDED = 0x00090313 SEC_I_COMPLETE_AND_CONTINUE = 0x00090314 SECBUFFER_VERSION = 0 SECBUFFER_TOKEN = 2 NTLMBUF_LEN = 12000 ) const ISC_REQ = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONNECTION | ISC_REQ_DELEGATE type SecurityFunctionTable struct { dwVersion uint32 EnumerateSecurityPackages uintptr QueryCredentialsAttributes uintptr AcquireCredentialsHandle uintptr FreeCredentialsHandle uintptr Reserved2 uintptr InitializeSecurityContext uintptr AcceptSecurityContext uintptr CompleteAuthToken uintptr DeleteSecurityContext uintptr ApplyControlToken uintptr QueryContextAttributes uintptr ImpersonateSecurityContext uintptr RevertSecurityContext uintptr MakeSignature uintptr VerifySignature uintptr FreeContextBuffer uintptr QuerySecurityPackageInfo uintptr Reserved3 uintptr Reserved4 uintptr Reserved5 uintptr Reserved6 uintptr Reserved7 uintptr Reserved8 uintptr QuerySecurityContextToken uintptr EncryptMessage uintptr DecryptMessage uintptr } type SEC_WINNT_AUTH_IDENTITY struct { User *uint16 UserLength uint32 Domain *uint16 DomainLength uint32 Password *uint16 PasswordLength uint32 Flags uint32 } type TimeStamp struct { LowPart uint32 HighPart int32 } type SecHandle struct { dwLower uintptr dwUpper uintptr } type SecBuffer struct { cbBuffer uint32 BufferType uint32 pvBuffer *byte } type SecBufferDesc struct { ulVersion uint32 cBuffers uint32 pBuffers *SecBuffer } type SSPIAuth struct { Domain string UserName string Password string Service string cred SecHandle ctxt SecHandle } func getAuth(user, password, service, workstation string) (auth, bool) { if user == "" { return &SSPIAuth{Service: service}, true } if !strings.ContainsRune(user, '\\') { return nil, false } domain_user := strings.SplitN(user, "\\", 2) return &SSPIAuth{ Domain: domain_user[0], UserName: domain_user[1], Password: password, Service: service, }, true } func (auth *SSPIAuth) InitialBytes() ([]byte, error) { var identity *SEC_WINNT_AUTH_IDENTITY if auth.UserName != "" { identity = &SEC_WINNT_AUTH_IDENTITY{ Flags: SEC_WINNT_AUTH_IDENTITY_UNICODE, Password: syscall.StringToUTF16Ptr(auth.Password), PasswordLength: uint32(len(auth.Password)), Domain: syscall.StringToUTF16Ptr(auth.Domain), DomainLength: uint32(len(auth.Domain)), User: syscall.StringToUTF16Ptr(auth.UserName), UserLength: uint32(len(auth.UserName)), } } var ts TimeStamp sec_ok, _, _ := syscall.Syscall9(sec_fn.AcquireCredentialsHandle, 9, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Negotiate"))), SECPKG_CRED_OUTBOUND, 0, uintptr(unsafe.Pointer(identity)), 0, 0, uintptr(unsafe.Pointer(&auth.cred)), uintptr(unsafe.Pointer(&ts))) if sec_ok != SEC_E_OK { return nil, fmt.Errorf("AcquireCredentialsHandle failed %x", sec_ok) } var buf SecBuffer var desc SecBufferDesc desc.ulVersion = SECBUFFER_VERSION desc.cBuffers = 1 desc.pBuffers = &buf outbuf := make([]byte, NTLMBUF_LEN) buf.cbBuffer = NTLMBUF_LEN buf.BufferType = SECBUFFER_TOKEN buf.pvBuffer = &outbuf[0] var attrs uint32 sec_ok, _, _ = syscall.Syscall12(sec_fn.InitializeSecurityContext, 12, uintptr(unsafe.Pointer(&auth.cred)), 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))), ISC_REQ, 0, SECURITY_NETWORK_DREP, 0, 0, uintptr(unsafe.Pointer(&auth.ctxt)), uintptr(unsafe.Pointer(&desc)), uintptr(unsafe.Pointer(&attrs)), uintptr(unsafe.Pointer(&ts))) if sec_ok == SEC_I_COMPLETE_AND_CONTINUE || sec_ok == SEC_I_COMPLETE_NEEDED { syscall.Syscall6(sec_fn.CompleteAuthToken, 2, uintptr(unsafe.Pointer(&auth.ctxt)), uintptr(unsafe.Pointer(&desc)), 0, 0, 0, 0) } else if sec_ok != SEC_E_OK && sec_ok != SEC_I_CONTINUE_NEEDED { syscall.Syscall6(sec_fn.FreeCredentialsHandle, 1, uintptr(unsafe.Pointer(&auth.cred)), 0, 0, 0, 0, 0) return nil, fmt.Errorf("InitialBytes InitializeSecurityContext failed %x", sec_ok) } return outbuf[:buf.cbBuffer], nil } func (auth *SSPIAuth) NextBytes(bytes []byte) ([]byte, error) { var in_buf, out_buf SecBuffer var in_desc, out_desc SecBufferDesc in_desc.ulVersion = SECBUFFER_VERSION in_desc.cBuffers = 1 in_desc.pBuffers = &in_buf out_desc.ulVersion = SECBUFFER_VERSION out_desc.cBuffers = 1 out_desc.pBuffers = &out_buf in_buf.BufferType = SECBUFFER_TOKEN in_buf.pvBuffer = &bytes[0] in_buf.cbBuffer = uint32(len(bytes)) outbuf := make([]byte, NTLMBUF_LEN) out_buf.BufferType = SECBUFFER_TOKEN out_buf.pvBuffer = &outbuf[0] out_buf.cbBuffer = NTLMBUF_LEN var attrs uint32 var ts TimeStamp sec_ok, _, _ := syscall.Syscall12(sec_fn.InitializeSecurityContext, 12, uintptr(unsafe.Pointer(&auth.cred)), uintptr(unsafe.Pointer(&auth.ctxt)), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))), ISC_REQ, 0, SECURITY_NETWORK_DREP, uintptr(unsafe.Pointer(&in_desc)), 0, uintptr(unsafe.Pointer(&auth.ctxt)), uintptr(unsafe.Pointer(&out_desc)), uintptr(unsafe.Pointer(&attrs)), uintptr(unsafe.Pointer(&ts))) if sec_ok == SEC_I_COMPLETE_AND_CONTINUE || sec_ok == SEC_I_COMPLETE_NEEDED { syscall.Syscall6(sec_fn.CompleteAuthToken, 2, uintptr(unsafe.Pointer(&auth.ctxt)), uintptr(unsafe.Pointer(&out_desc)), 0, 0, 0, 0) } else if sec_ok != SEC_E_OK && sec_ok != SEC_I_CONTINUE_NEEDED { return nil, fmt.Errorf("NextBytes InitializeSecurityContext failed %x", sec_ok) } return outbuf[:out_buf.cbBuffer], nil } func (auth *SSPIAuth) Free() { syscall.Syscall6(sec_fn.DeleteSecurityContext, 1, uintptr(unsafe.Pointer(&auth.ctxt)), 0, 0, 0, 0, 0) syscall.Syscall6(sec_fn.FreeCredentialsHandle, 1, uintptr(unsafe.Pointer(&auth.cred)), 0, 0, 0, 0, 0) }